Rethrow original BeanCreationException if fallback producer fails

Closes gh-22689
This commit is contained in:
Juergen Hoeller 2019-03-27 16:01:18 +01:00
parent c54355784e
commit ca24fd50b0
4 changed files with 95 additions and 30 deletions

View File

@ -26,6 +26,7 @@ import org.hibernate.resource.beans.container.spi.ContainedBean;
import org.hibernate.resource.beans.spi.BeanInstanceProducer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.lang.Nullable;
@ -155,7 +156,22 @@ public final class SpringBeanContainer implements BeanContainer {
logger.debug("Falling back to Hibernate's default producer after bean creation failure for " +
beanType + ": " + ex);
}
return new SpringContainedBean<>(fallbackProducer.produceBeanInstance(beanType));
try {
return new SpringContainedBean<>(fallbackProducer.produceBeanInstance(beanType));
}
catch (RuntimeException ex2) {
if (ex instanceof BeanCreationException) {
if (logger.isDebugEnabled()) {
logger.debug("Fallback producer failed for " + beanType + ": " + ex2);
}
// Rethrow original Spring exception from first attempt.
throw ex;
}
else {
// Throw fallback producer exception since original was probably NoSuchBeanDefinitionException.
throw ex2;
}
}
}
}
@ -177,9 +193,24 @@ public final class SpringBeanContainer implements BeanContainer {
catch (BeansException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Falling back to Hibernate's default producer after bean creation failure for " +
beanType + ": " + ex);
beanType + " with name '" + name + "': " + ex);
}
try {
return new SpringContainedBean<>(fallbackProducer.produceBeanInstance(name, beanType));
}
catch (RuntimeException ex2) {
if (ex instanceof BeanCreationException) {
if (logger.isDebugEnabled()) {
logger.debug("Fallback producer failed for " + beanType + " with name '" + name + "': " + ex2);
}
// Rethrow original Spring exception from first attempt.
throw ex;
}
else {
// Throw fallback producer exception since original was probably NoSuchBeanDefinitionException.
throw ex2;
}
}
return new SpringContainedBean<>(fallbackProducer.produceBeanInstance(name, beanType));
}
}

View File

@ -16,19 +16,24 @@
package org.springframework.orm.jpa.hibernate;
import javax.persistence.AttributeConverter;
import org.hibernate.SessionFactory;
import org.hibernate.resource.beans.container.spi.BeanContainer;
import org.hibernate.resource.beans.container.spi.ContainedBean;
import org.hibernate.resource.beans.spi.BeanInstanceProducer;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
import org.hibernate.service.ServiceRegistry;
import org.junit.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.orm.jpa.AbstractEntityManagerFactoryIntegrationTests;
import org.springframework.orm.jpa.hibernate.beans.*;
import org.springframework.orm.jpa.hibernate.beans.BeanSource;
import org.springframework.orm.jpa.hibernate.beans.MultiplePrototypesInSpringContextTestBean;
import org.springframework.orm.jpa.hibernate.beans.NoDefinitionInSpringContextTestBean;
import org.springframework.orm.jpa.hibernate.beans.SinglePrototypeInSpringContextTestBean;
import static org.junit.Assert.*;
@ -36,6 +41,7 @@ import static org.junit.Assert.*;
* Hibernate-specific SpringBeanContainer integration tests.
*
* @author Yoann Rodiere
* @author Juergen Hoeller
*/
public class HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTests
extends AbstractEntityManagerFactoryIntegrationTests {
@ -51,9 +57,9 @@ public class HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTe
}
private ManagedBeanRegistry getManagedBeanRegistry() {
SessionFactory sessionFactory = entityManagerFactory.unwrap( SessionFactory.class );
SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
ServiceRegistry serviceRegistry = sessionFactory.getSessionFactoryOptions().getServiceRegistry();
return serviceRegistry.requireService( ManagedBeanRegistry.class );
return serviceRegistry.requireService(ManagedBeanRegistry.class);
}
private BeanContainer getBeanContainer() {
@ -68,8 +74,7 @@ public class HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTe
ContainedBean<SinglePrototypeInSpringContextTestBean> bean = beanContainer.getBean(
SinglePrototypeInSpringContextTestBean.class,
JpaLifecycleOptions.INSTANCE,
IneffectiveBeanInstanceProducer.INSTANCE
JpaLifecycleOptions.INSTANCE, IneffectiveBeanInstanceProducer.INSTANCE
);
assertNotNull(bean);
@ -85,8 +90,7 @@ public class HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTe
ContainedBean<MultiplePrototypesInSpringContextTestBean> bean = beanContainer.getBean(
"multiple-1", MultiplePrototypesInSpringContextTestBean.class,
JpaLifecycleOptions.INSTANCE,
IneffectiveBeanInstanceProducer.INSTANCE
JpaLifecycleOptions.INSTANCE, IneffectiveBeanInstanceProducer.INSTANCE
);
assertNotNull(bean);
@ -103,8 +107,7 @@ public class HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTe
ContainedBean<SinglePrototypeInSpringContextTestBean> bean = beanContainer.getBean(
SinglePrototypeInSpringContextTestBean.class,
NativeLifecycleOptions.INSTANCE,
IneffectiveBeanInstanceProducer.INSTANCE
NativeLifecycleOptions.INSTANCE, IneffectiveBeanInstanceProducer.INSTANCE
);
assertNotNull(bean);
@ -115,8 +118,7 @@ public class HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTe
ContainedBean<SinglePrototypeInSpringContextTestBean> bean2 = beanContainer.getBean(
SinglePrototypeInSpringContextTestBean.class,
NativeLifecycleOptions.INSTANCE,
IneffectiveBeanInstanceProducer.INSTANCE
NativeLifecycleOptions.INSTANCE, IneffectiveBeanInstanceProducer.INSTANCE
);
assertNotNull(bean2);
@ -133,8 +135,7 @@ public class HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTe
ContainedBean<MultiplePrototypesInSpringContextTestBean> bean = beanContainer.getBean(
"multiple-1", MultiplePrototypesInSpringContextTestBean.class,
NativeLifecycleOptions.INSTANCE,
IneffectiveBeanInstanceProducer.INSTANCE
NativeLifecycleOptions.INSTANCE, IneffectiveBeanInstanceProducer.INSTANCE
);
assertNotNull(bean);
@ -145,8 +146,7 @@ public class HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTe
ContainedBean<MultiplePrototypesInSpringContextTestBean> bean2 = beanContainer.getBean(
"multiple-1", MultiplePrototypesInSpringContextTestBean.class,
NativeLifecycleOptions.INSTANCE,
IneffectiveBeanInstanceProducer.INSTANCE
NativeLifecycleOptions.INSTANCE, IneffectiveBeanInstanceProducer.INSTANCE
);
assertNotNull(bean2);
@ -164,8 +164,7 @@ public class HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTe
ContainedBean<NoDefinitionInSpringContextTestBean> bean = beanContainer.getBean(
NoDefinitionInSpringContextTestBean.class,
JpaLifecycleOptions.INSTANCE,
fallbackProducer
JpaLifecycleOptions.INSTANCE, fallbackProducer
);
assertEquals(1, fallbackProducer.currentUnnamedInstantiationCount());
@ -186,8 +185,7 @@ public class HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTe
ContainedBean<NoDefinitionInSpringContextTestBean> bean = beanContainer.getBean(
"some name", NoDefinitionInSpringContextTestBean.class,
JpaLifecycleOptions.INSTANCE,
fallbackProducer
JpaLifecycleOptions.INSTANCE, fallbackProducer
);
assertEquals(0, fallbackProducer.currentUnnamedInstantiationCount());
@ -209,8 +207,7 @@ public class HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTe
ContainedBean<NoDefinitionInSpringContextTestBean> bean = beanContainer.getBean(
NoDefinitionInSpringContextTestBean.class,
NativeLifecycleOptions.INSTANCE,
fallbackProducer
NativeLifecycleOptions.INSTANCE, fallbackProducer
);
assertEquals(1, fallbackProducer.currentUnnamedInstantiationCount());
@ -231,8 +228,7 @@ public class HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTe
ContainedBean<NoDefinitionInSpringContextTestBean> bean = beanContainer.getBean(
"some name", NoDefinitionInSpringContextTestBean.class,
NativeLifecycleOptions.INSTANCE,
fallbackProducer
NativeLifecycleOptions.INSTANCE, fallbackProducer
);
assertEquals(0, fallbackProducer.currentUnnamedInstantiationCount());
@ -246,11 +242,40 @@ public class HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTe
assertNull(instance.getApplicationContext());
}
@Test(expected = UnsupportedOperationException.class)
public void testFallbackExceptionInCaseOfNoSpringBeanFound() {
getBeanContainer().getBean(NoDefinitionInSpringContextTestBean.class,
NativeLifecycleOptions.INSTANCE, IneffectiveBeanInstanceProducer.INSTANCE
);
}
@Test(expected = BeanCreationException.class)
public void testOriginalExceptionInCaseOfFallbackProducerFailure() {
getBeanContainer().getBean(AttributeConverter.class,
NativeLifecycleOptions.INSTANCE, IneffectiveBeanInstanceProducer.INSTANCE
);
}
@Test(expected = UnsupportedOperationException.class)
public void testFallbackExceptionInCaseOfNoSpringBeanFoundByName() {
getBeanContainer().getBean("some name", NoDefinitionInSpringContextTestBean.class,
NativeLifecycleOptions.INSTANCE, IneffectiveBeanInstanceProducer.INSTANCE
);
}
@Test(expected = BeanCreationException.class)
public void testOriginalExceptionInCaseOfFallbackProducerFailureByName() {
getBeanContainer().getBean("invalid", AttributeConverter.class,
NativeLifecycleOptions.INSTANCE, IneffectiveBeanInstanceProducer.INSTANCE
);
}
/**
* The lifecycle options mandated by the JPA spec and used as a default in Hibernate ORM.
*/
private static class JpaLifecycleOptions implements BeanContainer.LifecycleOptions {
public static final JpaLifecycleOptions INSTANCE = new JpaLifecycleOptions();
@Override
@ -264,12 +289,14 @@ public class HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTe
}
}
/**
* The lifecycle options used by libraries integrating into Hibernate ORM
* and that want a behavior closer to Spring's native behavior,
* such as Hibernate Search.
*/
private static class NativeLifecycleOptions implements BeanContainer.LifecycleOptions {
public static final NativeLifecycleOptions INSTANCE = new NativeLifecycleOptions();
@Override
@ -283,7 +310,9 @@ public class HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTe
}
}
private static class IneffectiveBeanInstanceProducer implements BeanInstanceProducer {
public static final IneffectiveBeanInstanceProducer INSTANCE = new IneffectiveBeanInstanceProducer();
@Override
@ -297,8 +326,11 @@ public class HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTe
}
}
private static class NoDefinitionInSpringContextTestBeanInstanceProducer implements BeanInstanceProducer {
private int unnamedInstantiationCount = 0;
private int namedInstantiationCount = 0;
@Override
@ -341,4 +373,5 @@ public class HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTe
return namedInstantiationCount;
}
}
}

View File

@ -19,9 +19,8 @@ package org.springframework.orm.jpa.hibernate.beans;
public class NoDefinitionInSpringContextTestBean extends TestBean {
private NoDefinitionInSpringContextTestBean() {
throw new AssertionError(
"Unexpected call to the default constructor."
+ " Is Spring trying to instantiate this class by itself, even though it should delegate to the fallback producer?"
throw new AssertionError("Unexpected call to the default constructor. " +
"Is Spring trying to instantiate this class by itself, even though it should delegate to the fallback producer?"
);
}

View File

@ -28,4 +28,6 @@
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
<bean id="invalid" class="javax.persistence.AttributeConverter" lazy-init="true"/>
</beans>