Refresh/rebuild 集成测试期间的特定 bean
Refresh/rebuild specific beans during Spring integration testing
我们现有的 Spring 引导集成设置使用 @DirtiesContext
在不同的测试方法之间重建整个 bean 池。
这相当慢,因此我们开始使用可能 "refreshed" 或撕裂 down/rebuild 内部 的 bean,而无需重新创建实例。
问题是只有一些bean支持这个。如果我们控制UsersBean
,我们可以实现一个UsersBean.refresh()
方法,在我们的@After
方法中调用
但是如果我们现有的beans/classes不支持刷新,或者我们不控制,我们如何有条件地指示某些bean需要dirtied/rebuilt具体测试后?
或更简洁:有没有办法在测试方法结束时将 bean 池的 子部分 标记为脏,以便重建?
看起来这是可能的,至少在 Spring 引导环境中。 ApplicationContext
实现有一个 GenericApplicationContext which has the ability to removeBeanDefinition(), which can then be re-registered via registerBeanDefinition().
这甚至通过级联来删除持有对被删除的 bean 的引用的 bean(可以在 DefaultSingletonBeanRegistry.destroyBean() 中看到它的实现)。
例如,如果 Bean1
被 Bean2
引用:
@Component
public class Bean1 {
}
@Component
public class Bean2 {
@Autowired
public Bean1 bean1;
}
然后测试可以从上下文中删除 bean1
,并看到 bean2
也被替换:
@RunWith(SpringRunner.class)
public class BeanRemovalTest implements ApplicationContextAware {
@Autowired
private Bean1 bean1;
@Autowired
private Bean2 bean2;
private ApplicationContext applicationContext;
@Test
public void test1() throws Exception {
System.out.println("test1():");
System.out.println(" bean1=" + bean1);
System.out.println(" bean2.bean1=" + bean2.bean1);
resetBean("bean1");
}
@Test
public void test2() throws Exception {
System.out.println("test2():");
System.out.println(" bean1=" + bean1);
System.out.println(" bean2.bean1=" + bean2.bean1);
}
private void resetBean(String beanName) {
GenericApplicationContext genericApplicationContext = (GenericApplicationContext) applicationContext;
BeanDefinition bd = genericApplicationContext
.getBeanDefinition(beanName);
genericApplicationContext.removeBeanDefinition("bean1");
genericApplicationContext.registerBeanDefinition("bean1", bd);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
}
这显示两个 bean 实例都被替换了:
test1():
bean1=hello.so.Bean1@61d6015a
bean2.bean1=hello.so.Bean1@61d6015a
test2():
bean1=hello.so.Bean1@2e570ded
bean2.bean1=hello.so.Bean1@2e570ded
(如果resetBean("bean1")
被注释掉,则两次都是同一个实例)
肯定有一些边缘是行不通的——例如如果另一个 bean 持有从 ApplicationContext.getBean()
.
获得的引用
我们现有的 Spring 引导集成设置使用 @DirtiesContext
在不同的测试方法之间重建整个 bean 池。
这相当慢,因此我们开始使用可能 "refreshed" 或撕裂 down/rebuild 内部 的 bean,而无需重新创建实例。
问题是只有一些bean支持这个。如果我们控制UsersBean
,我们可以实现一个UsersBean.refresh()
方法,在我们的@After
方法中调用
但是如果我们现有的beans/classes不支持刷新,或者我们不控制,我们如何有条件地指示某些bean需要dirtied/rebuilt具体测试后?
或更简洁:有没有办法在测试方法结束时将 bean 池的 子部分 标记为脏,以便重建?
看起来这是可能的,至少在 Spring 引导环境中。 ApplicationContext
实现有一个 GenericApplicationContext which has the ability to removeBeanDefinition(), which can then be re-registered via registerBeanDefinition().
这甚至通过级联来删除持有对被删除的 bean 的引用的 bean(可以在 DefaultSingletonBeanRegistry.destroyBean() 中看到它的实现)。
例如,如果 Bean1
被 Bean2
引用:
@Component
public class Bean1 {
}
@Component
public class Bean2 {
@Autowired
public Bean1 bean1;
}
然后测试可以从上下文中删除 bean1
,并看到 bean2
也被替换:
@RunWith(SpringRunner.class)
public class BeanRemovalTest implements ApplicationContextAware {
@Autowired
private Bean1 bean1;
@Autowired
private Bean2 bean2;
private ApplicationContext applicationContext;
@Test
public void test1() throws Exception {
System.out.println("test1():");
System.out.println(" bean1=" + bean1);
System.out.println(" bean2.bean1=" + bean2.bean1);
resetBean("bean1");
}
@Test
public void test2() throws Exception {
System.out.println("test2():");
System.out.println(" bean1=" + bean1);
System.out.println(" bean2.bean1=" + bean2.bean1);
}
private void resetBean(String beanName) {
GenericApplicationContext genericApplicationContext = (GenericApplicationContext) applicationContext;
BeanDefinition bd = genericApplicationContext
.getBeanDefinition(beanName);
genericApplicationContext.removeBeanDefinition("bean1");
genericApplicationContext.registerBeanDefinition("bean1", bd);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
}
这显示两个 bean 实例都被替换了:
test1():
bean1=hello.so.Bean1@61d6015a
bean2.bean1=hello.so.Bean1@61d6015a
test2():
bean1=hello.so.Bean1@2e570ded
bean2.bean1=hello.so.Bean1@2e570ded
(如果resetBean("bean1")
被注释掉,则两次都是同一个实例)
肯定有一些边缘是行不通的——例如如果另一个 bean 持有从 ApplicationContext.getBean()
.