在运行时替换bean

Jat*_*tin 8 java spring unit-testing

代码库是典型的基于Spring的企业代码库,具有大约150万行代码.我们有很多弹簧上下文文件.测试基础是一个问题.

对于测试用例,我创建了另一组测试弹簧文件(主要是导入相关的项目弹簧上下文),而少数bean包含用于外部服务的模拟bean.所有测试类都使用相同的上下文配置文件集,并且90%的时间都是如此.

但在某些情况下,会有一个我想嘲笑的bean.但是我不想编辑spring-text.xml(因为它会干扰所有类),我也不希望每个测试类都有单独的xml集.一个非常简单的说法是:

@Autowired
@Qualifier("fundManager")
FundManager fundManager;

@Test
public void testSomething(){
    TransactionManager tx = mock(TransactionManager.class);
    fundManager.setTransactionManager(tx);
    //now all is well.
}
Run Code Online (Sandbox Code Playgroud)

这在某些情况下有效.但有时候,希望在整个代码库中使用bean的tx地方设置这个新的临时bean TransactionManager.

代理类恕我直言不是一个很好的解决方案,因为我必须用包装器包装所有bean.这是我理想的寻求:

@Test
public void testSomething(){
    TransactionManager tx = mock(TransactionManager.class);
    replace("transactionManagerBean",tx);
    //For bean with id:transactionManagerBean should be replace with `tx`
}
Run Code Online (Sandbox Code Playgroud)

BeanPostProcessor 看起来像一个替代的建议,但我遇到了一些打嗝.

urs*_*rsa 5

想象一下,您已经将bean A注入到bean B中:

public static class A {
}

public static class B {
    @Autowired
    private A a;

    @Override
    public String toString() {
        return "B [a=" + a + ']';
    }
}
Run Code Online (Sandbox Code Playgroud)

然后用Spring上下文初始化您的应用程序:

<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
    <context:annotation-config/>
    <bean id="a" class="test.Test$A"/>
    <bean id="b" class="test.Test$B"/>
</beans>
Run Code Online (Sandbox Code Playgroud)

然后,以下代码片段将替换上下文中的所有bean A:

public static void main(String[] args) throws Exception {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("test.xml");

    System.out.println(ctx.getBean("b"));

    final A replacement = new A();
    for (String name : ctx.getBeanDefinitionNames()) {
        final Object bean = ctx.getBean(name);
        ReflectionUtils.doWithFields(bean.getClass(),
            field -> {
                field.setAccessible(true);
                field.set(bean, replacement);
            },
            // Here you can provide your filtering.
            field -> field.getType().equals(A.class)
        );
    }

    System.out.println(ctx.getBean("b"));
}
Run Code Online (Sandbox Code Playgroud)

该示例是使用Java 8 + Spring 4.1制作的。但是,修改Java和Spring的旧版本的代码很简单。