提交 00855c4f 编写于 作者: Y Yoann Rodière 提交者: Juergen Hoeller

Add tests for SpringBeanContainer (Hibernate ORM integration) and fix the...

Add tests for SpringBeanContainer (Hibernate ORM integration) and fix the behavior when requesting named beans (#22260)

* Add integration tests for SpringBeanContainer (Hibernate ORM integration)
* Autowire bean properties of beans retrieved by name in SpringBeanContainer
* Add integration tests for fallback cases in SpringBeanContainer (Hibernate ORM integration)
* Fix SpringBeanContainer incorrectly losing the bean name when calling the fallback producer
上级 4c9ae649
......@@ -115,11 +115,6 @@ public final class SpringBeanContainer implements BeanContainer {
@SuppressWarnings("unchecked")
public <B> ContainedBean<B> getBean(
String name, Class<B> beanType, LifecycleOptions lifecycleOptions, BeanInstanceProducer fallbackProducer) {
if (!this.beanFactory.containsBean(name)) {
return getBean(beanType, lifecycleOptions, fallbackProducer);
}
SpringContainedBean<?> bean;
if (lifecycleOptions.canUseCachedReferences()) {
bean = this.beanCache.get(name);
......@@ -169,6 +164,7 @@ public final class SpringBeanContainer implements BeanContainer {
try {
if (lifecycleOptions.useJpaCompliantCreation()) {
Object bean = this.beanFactory.autowire(beanType, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, false);
this.beanFactory.autowireBeanProperties(bean, AutowireCapableBeanFactory.AUTOWIRE_NO, false);
this.beanFactory.applyBeanPropertyValues(bean, name);
bean = this.beanFactory.initializeBean(bean, name);
return new SpringContainedBean<>(bean, beanInstance -> this.beanFactory.destroyBean(name, beanInstance));
......
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jpa.hibernate;
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.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.orm.jpa.AbstractEntityManagerFactoryIntegrationTests;
import org.springframework.orm.jpa.hibernate.beans.*;
import static org.junit.Assert.*;
/**
* Hibernate-specific SpringBeanContainer integration tests.
*
* @author Yoann Rodiere
*/
public class HibernateNativeEntityManagerFactorySpringBeanContainerIntegrationTests
extends AbstractEntityManagerFactoryIntegrationTests {
@Autowired
private ApplicationContext applicationContext;
@Override
protected String[] getConfigLocations() {
return new String[] {"/org/springframework/orm/jpa/hibernate/hibernate-manager-native.xml",
"/org/springframework/orm/jpa/memdb.xml", "/org/springframework/orm/jpa/inject.xml",
"/org/springframework/orm/jpa/hibernate/inject-hibernate-spring-bean-container-tests.xml"};
}
private ManagedBeanRegistry getManagedBeanRegistry() {
SessionFactory sessionFactory = entityManagerFactory.unwrap( SessionFactory.class );
ServiceRegistry serviceRegistry = sessionFactory.getSessionFactoryOptions().getServiceRegistry();
return serviceRegistry.requireService( ManagedBeanRegistry.class );
}
private BeanContainer getBeanContainer() {
return getManagedBeanRegistry().getBeanContainer();
}
@Test
public void testCanRetrieveBeanByTypeWithJpaCompliantOptions() {
BeanContainer beanContainer = getBeanContainer();
assertNotNull(beanContainer);
ContainedBean<SinglePrototypeInSpringContextTestBean> bean = beanContainer.getBean(
SinglePrototypeInSpringContextTestBean.class,
JpaLifecycleOptions.INSTANCE,
IneffectiveBeanInstanceProducer.INSTANCE
);
assertNotNull(bean);
SinglePrototypeInSpringContextTestBean instance = bean.getBeanInstance();
assertNotNull(instance);
assertSame(applicationContext, instance.getApplicationContext());
}
@Test
public void testCanRetrieveBeanByNameWithJpaCompliantOptions() {
BeanContainer beanContainer = getBeanContainer();
assertNotNull(beanContainer);
ContainedBean<MultiplePrototypesInSpringContextTestBean> bean = beanContainer.getBean(
"multiple-1", MultiplePrototypesInSpringContextTestBean.class,
JpaLifecycleOptions.INSTANCE,
IneffectiveBeanInstanceProducer.INSTANCE
);
assertNotNull(bean);
MultiplePrototypesInSpringContextTestBean instance = bean.getBeanInstance();
assertNotNull(instance);
assertEquals("multiple-1", instance.getName());
assertSame(applicationContext, instance.getApplicationContext());
}
@Test
public void testCanRetrieveBeanByTypeWithNativeOptions() {
BeanContainer beanContainer = getBeanContainer();
assertNotNull(beanContainer);
ContainedBean<SinglePrototypeInSpringContextTestBean> bean = beanContainer.getBean(
SinglePrototypeInSpringContextTestBean.class,
NativeLifecycleOptions.INSTANCE,
IneffectiveBeanInstanceProducer.INSTANCE
);
assertNotNull(bean);
SinglePrototypeInSpringContextTestBean instance = bean.getBeanInstance();
assertNotNull(instance);
assertEquals("single", instance.getName());
assertSame(applicationContext, instance.getApplicationContext());
ContainedBean<SinglePrototypeInSpringContextTestBean> bean2 = beanContainer.getBean(
SinglePrototypeInSpringContextTestBean.class,
NativeLifecycleOptions.INSTANCE,
IneffectiveBeanInstanceProducer.INSTANCE
);
assertNotNull(bean2);
SinglePrototypeInSpringContextTestBean instance2 = bean2.getBeanInstance();
assertNotNull(instance2);
// Due to the lifecycle options, and because the bean has the "prototype" scope, we should not return the same instance
assertNotSame(instance, instance2);
}
@Test
public void testCanRetrieveBeanByNameWithNativeOptions() {
BeanContainer beanContainer = getBeanContainer();
assertNotNull(beanContainer);
ContainedBean<MultiplePrototypesInSpringContextTestBean> bean = beanContainer.getBean(
"multiple-1", MultiplePrototypesInSpringContextTestBean.class,
NativeLifecycleOptions.INSTANCE,
IneffectiveBeanInstanceProducer.INSTANCE
);
assertNotNull(bean);
MultiplePrototypesInSpringContextTestBean instance = bean.getBeanInstance();
assertNotNull(instance);
assertEquals("multiple-1", instance.getName());
assertSame(applicationContext, instance.getApplicationContext());
ContainedBean<MultiplePrototypesInSpringContextTestBean> bean2 = beanContainer.getBean(
"multiple-1", MultiplePrototypesInSpringContextTestBean.class,
NativeLifecycleOptions.INSTANCE,
IneffectiveBeanInstanceProducer.INSTANCE
);
assertNotNull(bean2);
MultiplePrototypesInSpringContextTestBean instance2 = bean2.getBeanInstance();
assertNotNull(instance2);
// Due to the lifecycle options, and because the bean has the "prototype" scope, we should not return the same instance
assertNotSame(instance, instance2);
}
@Test
public void testCanRetrieveFallbackBeanByTypeWithJpaCompliantOptions() {
BeanContainer beanContainer = getBeanContainer();
assertNotNull(beanContainer);
NoDefinitionInSpringContextTestBeanInstanceProducer fallbackProducer = new NoDefinitionInSpringContextTestBeanInstanceProducer();
ContainedBean<NoDefinitionInSpringContextTestBean> bean = beanContainer.getBean(
NoDefinitionInSpringContextTestBean.class,
JpaLifecycleOptions.INSTANCE,
fallbackProducer
);
assertEquals(1, fallbackProducer.currentUnnamedInstantiationCount());
assertEquals(0, fallbackProducer.currentNamedInstantiationCount());
assertNotNull(bean);
NoDefinitionInSpringContextTestBean instance = bean.getBeanInstance();
assertNotNull(instance);
assertEquals(BeanSource.FALLBACK, instance.getSource());
assertNull(instance.getApplicationContext());
}
@Test
public void testCanRetrieveFallbackBeanByNameWithJpaCompliantOptions() {
BeanContainer beanContainer = getBeanContainer();
assertNotNull(beanContainer);
NoDefinitionInSpringContextTestBeanInstanceProducer fallbackProducer = new NoDefinitionInSpringContextTestBeanInstanceProducer();
ContainedBean<NoDefinitionInSpringContextTestBean> bean = beanContainer.getBean(
"some name", NoDefinitionInSpringContextTestBean.class,
JpaLifecycleOptions.INSTANCE,
fallbackProducer
);
assertEquals(0, fallbackProducer.currentUnnamedInstantiationCount());
assertEquals(1, fallbackProducer.currentNamedInstantiationCount());
assertNotNull(bean);
NoDefinitionInSpringContextTestBean instance = bean.getBeanInstance();
assertNotNull(instance);
assertEquals(BeanSource.FALLBACK, instance.getSource());
assertEquals("some name", instance.getName());
assertNull(instance.getApplicationContext());
}
@Test
public void testCanRetrieveFallbackBeanByTypeWithNativeOptions() {
BeanContainer beanContainer = getBeanContainer();
assertNotNull(beanContainer);
NoDefinitionInSpringContextTestBeanInstanceProducer fallbackProducer = new NoDefinitionInSpringContextTestBeanInstanceProducer();
ContainedBean<NoDefinitionInSpringContextTestBean> bean = beanContainer.getBean(
NoDefinitionInSpringContextTestBean.class,
NativeLifecycleOptions.INSTANCE,
fallbackProducer
);
assertEquals(1, fallbackProducer.currentUnnamedInstantiationCount());
assertEquals(0, fallbackProducer.currentNamedInstantiationCount());
assertNotNull(bean);
NoDefinitionInSpringContextTestBean instance = bean.getBeanInstance();
assertNotNull(instance);
assertEquals(BeanSource.FALLBACK, instance.getSource());
assertNull(instance.getApplicationContext());
}
@Test
public void testCanRetrieveFallbackBeanByNameWithNativeOptions() {
BeanContainer beanContainer = getBeanContainer();
assertNotNull(beanContainer);
NoDefinitionInSpringContextTestBeanInstanceProducer fallbackProducer = new NoDefinitionInSpringContextTestBeanInstanceProducer();
ContainedBean<NoDefinitionInSpringContextTestBean> bean = beanContainer.getBean(
"some name", NoDefinitionInSpringContextTestBean.class,
NativeLifecycleOptions.INSTANCE,
fallbackProducer
);
assertEquals(0, fallbackProducer.currentUnnamedInstantiationCount());
assertEquals(1, fallbackProducer.currentNamedInstantiationCount());
assertNotNull(bean);
NoDefinitionInSpringContextTestBean instance = bean.getBeanInstance();
assertNotNull(instance);
assertEquals(BeanSource.FALLBACK, instance.getSource());
assertEquals("some name", instance.getName());
assertNull(instance.getApplicationContext());
}
/**
* 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
public boolean canUseCachedReferences() {
return true;
}
@Override
public boolean useJpaCompliantCreation() {
return true;
}
}
/**
* 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
public boolean canUseCachedReferences() {
return false;
}
@Override
public boolean useJpaCompliantCreation() {
return false;
}
}
private static class IneffectiveBeanInstanceProducer implements BeanInstanceProducer {
public static final IneffectiveBeanInstanceProducer INSTANCE = new IneffectiveBeanInstanceProducer();
@Override
public <B> B produceBeanInstance(Class<B> aClass) {
throw new UnsupportedOperationException("should not be called");
}
@Override
public <B> B produceBeanInstance(String s, Class<B> aClass) {
throw new UnsupportedOperationException("should not be called");
}
}
private static class NoDefinitionInSpringContextTestBeanInstanceProducer implements BeanInstanceProducer {
private int unnamedInstantiationCount = 0;
private int namedInstantiationCount = 0;
@Override
public <B> B produceBeanInstance(Class<B> beanType) {
try {
++unnamedInstantiationCount;
/*
* We only expect to ever be asked to instantiate this class, so we just cut corners here.
* A real-world implementation would obviously be different.
*/
NoDefinitionInSpringContextTestBean instance = new NoDefinitionInSpringContextTestBean(null, BeanSource.FALLBACK);
return beanType.cast( instance );
}
catch (RuntimeException e) {
throw new AssertionError( "Unexpected error instantiating a bean by type using reflection", e );
}
}
@Override
public <B> B produceBeanInstance(String name, Class<B> beanType) {
try {
++namedInstantiationCount;
/*
* We only expect to ever be asked to instantiate this class, so we just cut corners here.
* A real-world implementation would obviously be different.
*/
NoDefinitionInSpringContextTestBean instance = new NoDefinitionInSpringContextTestBean(name, BeanSource.FALLBACK);
return beanType.cast( instance );
}
catch (RuntimeException e) {
throw new AssertionError( "Unexpected error instantiating a bean by name using reflection", e );
}
}
private int currentUnnamedInstantiationCount() {
return unnamedInstantiationCount;
}
private int currentNamedInstantiationCount() {
return namedInstantiationCount;
}
}
}
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jpa.hibernate.beans;
public enum BeanSource {
SPRING,
FALLBACK;
}
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jpa.hibernate.beans;
public class MultiplePrototypesInSpringContextTestBean extends TestBean {
}
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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?"
);
}
/*
* Expect instantiation through a non-default constructor, just to be sure that Spring will fail if it tries to instantiate it,
* and will subsequently delegate to the fallback bean instance producer.
*/
public NoDefinitionInSpringContextTestBean(String name, BeanSource source) {
setName(name);
setSource(source);
}
}
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jpa.hibernate.beans;
public class SinglePrototypeInSpringContextTestBean extends TestBean {
}
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jpa.hibernate.beans;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
public abstract class TestBean {
private BeanSource source;
private String name;
@Autowired
private ApplicationContext applicationContext;
public BeanSource getSource() {
return source;
}
public void setSource(BeanSource source) {
this.source = source;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ApplicationContext getApplicationContext() {
return applicationContext;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<bean id="single" class="org.springframework.orm.jpa.hibernate.beans.SinglePrototypeInSpringContextTestBean" scope="prototype">
<property name="source" value="SPRING" />
<property name="name" value="single" />
</bean>
<bean id="multiple-1" class="org.springframework.orm.jpa.hibernate.beans.MultiplePrototypesInSpringContextTestBean" scope="prototype">
<property name="source" value="SPRING" />
<property name="name" value="multiple-1" />
</bean>
<bean id="multiple-2" class="org.springframework.orm.jpa.hibernate.beans.MultiplePrototypesInSpringContextTestBean" scope="prototype">
<property name="source" value="SPRING" />
<property name="name" value="multiple-2" />
</bean>
</beans>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册