diff --git a/spring-orm/src/main/java/org/springframework/orm/hibernate5/SpringBeanContainer.java b/spring-orm/src/main/java/org/springframework/orm/hibernate5/SpringBeanContainer.java index 1a05a6fb3e3..38e606c0227 100644 --- a/spring-orm/src/main/java/org/springframework/orm/hibernate5/SpringBeanContainer.java +++ b/spring-orm/src/main/java/org/springframework/orm/hibernate5/SpringBeanContainer.java @@ -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)); } } diff --git a/spring-orm/src/test/java/org/springframework/orm/jpa/hibernate/HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTests.java b/spring-orm/src/test/java/org/springframework/orm/jpa/hibernate/HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTests.java index 6620720c4f7..6388c7ec971 100644 --- a/spring-orm/src/test/java/org/springframework/orm/jpa/hibernate/HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTests.java +++ b/spring-orm/src/test/java/org/springframework/orm/jpa/hibernate/HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTests.java @@ -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 bean = beanContainer.getBean( SinglePrototypeInSpringContextTestBean.class, - JpaLifecycleOptions.INSTANCE, - IneffectiveBeanInstanceProducer.INSTANCE + JpaLifecycleOptions.INSTANCE, IneffectiveBeanInstanceProducer.INSTANCE ); assertNotNull(bean); @@ -85,8 +90,7 @@ public class HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTe ContainedBean 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 bean = beanContainer.getBean( SinglePrototypeInSpringContextTestBean.class, - NativeLifecycleOptions.INSTANCE, - IneffectiveBeanInstanceProducer.INSTANCE + NativeLifecycleOptions.INSTANCE, IneffectiveBeanInstanceProducer.INSTANCE ); assertNotNull(bean); @@ -115,8 +118,7 @@ public class HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTe ContainedBean bean2 = beanContainer.getBean( SinglePrototypeInSpringContextTestBean.class, - NativeLifecycleOptions.INSTANCE, - IneffectiveBeanInstanceProducer.INSTANCE + NativeLifecycleOptions.INSTANCE, IneffectiveBeanInstanceProducer.INSTANCE ); assertNotNull(bean2); @@ -133,8 +135,7 @@ public class HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTe ContainedBean 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 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 bean = beanContainer.getBean( NoDefinitionInSpringContextTestBean.class, - JpaLifecycleOptions.INSTANCE, - fallbackProducer + JpaLifecycleOptions.INSTANCE, fallbackProducer ); assertEquals(1, fallbackProducer.currentUnnamedInstantiationCount()); @@ -186,8 +185,7 @@ public class HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTe ContainedBean 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 bean = beanContainer.getBean( NoDefinitionInSpringContextTestBean.class, - NativeLifecycleOptions.INSTANCE, - fallbackProducer + NativeLifecycleOptions.INSTANCE, fallbackProducer ); assertEquals(1, fallbackProducer.currentUnnamedInstantiationCount()); @@ -231,8 +228,7 @@ public class HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTe ContainedBean 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; } } + } diff --git a/spring-orm/src/test/java/org/springframework/orm/jpa/hibernate/beans/NoDefinitionInSpringContextTestBean.java b/spring-orm/src/test/java/org/springframework/orm/jpa/hibernate/beans/NoDefinitionInSpringContextTestBean.java index 9953b0d77ff..145723edf7e 100644 --- a/spring-orm/src/test/java/org/springframework/orm/jpa/hibernate/beans/NoDefinitionInSpringContextTestBean.java +++ b/spring-orm/src/test/java/org/springframework/orm/jpa/hibernate/beans/NoDefinitionInSpringContextTestBean.java @@ -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?" ); } diff --git a/spring-orm/src/test/resources/org/springframework/orm/jpa/hibernate/hibernate-manager-native.xml b/spring-orm/src/test/resources/org/springframework/orm/jpa/hibernate/hibernate-manager-native.xml index 8944d5adaeb..1a6eeeafba5 100644 --- a/spring-orm/src/test/resources/org/springframework/orm/jpa/hibernate/hibernate-manager-native.xml +++ b/spring-orm/src/test/resources/org/springframework/orm/jpa/hibernate/hibernate-manager-native.xml @@ -28,4 +28,6 @@ + +