在运行时替换bean [英] Replace bean at runtime

查看:169
本文介绍了在运行时替换bean的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

The codebase is typical spring based enterprise codebase with about 1.5m lines of code. We have quite a few spring context files. The test infra is a problem.

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

For the test cases, I created another set of test-spring files (mostly it imports relevant project spring contexts) and for few beans contains mocked beans for external services. All Test classes use the same set of context configuration files and things are well 90% of the times.

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

But in some cases, there would be a bean which I would like to mock. But I do not wish to edit the spring-text.xml (as it will disturb all classes), nor do I wish to have separate set of xml's for each test class. One very simple say of doing it would be:

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

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

这在某些情况下有效。但有时,希望这个新的临时bean tx 应该设置在哪里 TransactionManager bean正在全部使用代码库。

This works in some cases. But sometimes, it is desired that this new temporary bean tx should be set where ever TransactionManager bean was being used all across the code base.

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

Proxy class IMHO is not a great solution, because I would then have to wrap all the beans with a wrapper. This is what I am ideally looking for:

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

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

BeanPostProcessor looks like an alternate suggestion but I have faced a few hiccups with it.

推荐答案

想象一下你有bean A注入bean B:

Imagine you have bean A injected into bean B:

public static class A {
}

public static class B {
    @Autowired
    private A a;

    @Override
    public String toString() {
        return "B [a=" + a + ']';
    }
}

初始化应用程序的弹出上下文:

And spring context to initialize your application:

<?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>

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

Then the following snippet will replace all beans A across the context:

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"));
}

这个例子是用Java 8 + Spring 4.1制作的。但是,修改Java和Spring的旧版本的代码会很简单。

This example was made with Java 8 + Spring 4.1. However it would be simple to modify the code for the elder versions of both Java and Spring.

这篇关于在运行时替换bean的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆