【问题标题】:Refresh/rebuild specific beans during Spring integration testing在 Spring 集成测试期间刷新/重建特定 bean
【发布时间】:2019-04-12 06:42:05
【问题描述】:

我们现有的 Spring Boot 集成设置使用 @DirtiesContext 在不同的测试方法之间重建整个 bean 池。

这相当慢,因此我们开始使用可以“刷新”或拆除/重建内部的 bean,而无需重新创建实例。

问题是只有一些 bean 支持这一点。如果我们控制UsersBean,我们可以实现一个UsersBean.refresh()方法并在我们的@After方法中调用它。

但是,如果我们现有的 bean/类不支持刷新,或者我们无法控制,我们如何有条件地指示某些 bean 在特定测试后需要被弄脏/重建?

或者更简洁地说:有没有办法在测试方法结束时将 bean 池的 subsection 标记为脏,以便重建?

【问题讨论】:

    标签: java spring spring-boot junit integration-testing


    【解决方案1】:

    看起来这是可能的,至少在 Spring Boot 环境中是这样。 ApplicationContext 的实现有一个GenericApplicationContext,它具有removeBeanDefinition() 的能力,然后可以通过registerBeanDefinition() 重新注册。

    这甚至可以级联删除持有对要删除的 bean 的引用的 bean(其实现可以在 DefaultSingletonBeanRegistry.destroyBean() 中看到)。

    例如,如果Bean1Bean2 引用:

    @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() 获得的引用。

    【讨论】:

    • 谢谢,这看起来很完美。我将对其进行测试,并允许在 19 小时内奖励赏金。
    猜你喜欢
    • 1970-01-01
    • 2018-11-09
    • 1970-01-01
    • 2017-10-29
    • 1970-01-01
    • 1970-01-01
    • 2015-03-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多