在运行时更换弹簧容器内的bean

时间:2012-02-05 06:13:08

标签: java spring

假设我在Spring容器中定义了一个bean(例如BeanA),并将此bean注入到一个对象中。 (例如BeanAUser)

在运行期间,我可以使用另一个bean实例来替换spring容器内的原始BeanA吗?并且还将这个新的bean实例重新注入BeanAUser以替换原来的BeanA?

7 个答案:

答案 0 :(得分:14)

使用代理可以轻松实现。创建一个委托给它的接口和切换对象的委托实现。

@Component("BeanA")
public class MyClass implements MyInterface {
  private MyInterface target;

  public void setTarget(MyInterface target) {
    this.target = target;
  }

  // now delegating implementation of MyInterface methods
  public void method1(..) {
    this.target.method1(..);
  }

  ..
}

答案 1 :(得分:6)

我这样做的方法是使用一个名为任意方法替换的系统。

创建一个实现org.springframework.beans.factory.support.MethodReplacer的类,这会强制您创建一个类似的方法

public Object reimplement(Object o, Method m, Object[] args) throws Throwable

参数表示以下内容:

  • o - 您正在替换
  • 上的方法的bean实例
  • m - 我们正在替换的方法元
  • args - 提供的方法参数(如果有的话)

所以我想你的课看起来像下面的

public BeanAUserHelper implements MethodReplacer {

    public Object reimplement(Object o, Method m, Object[] args) throws Throwable {

        if (some expression){
            return beanA;
        }
        else {
            return beanB;
        }
    }
}

在bean配置中,然后指示Spring替换getBeanX()上的BeanAUser方法,如此

<!-- this is the bean who needs to get a different instance -->
<bean id="beanAUser" class="a.b.c.BeanAUser">
    <!-- arbitrary method replacement -->
    <replaced-method name="getBeanX" replacer="beanAUserHelper"/>
</bean>

<!-- this is your 'dynamic bean getter' -->
<bean id="beanAUserHelper" class="a.b.c.BeanAUserHelper"/>

我希望我能正确理解你的问题:)

答案 2 :(得分:6)

Spring在运行时引入了新的RefreshScope来替换bean。在内部,按照answer of mrembisz

中的描述创建代理
@RefreshScope
@Component
public class MyBean { ... }

答案 3 :(得分:4)

假设mrembisz的答案中的MyClass不是最终的,那么人们不必手动实现装饰器模式,并且可以使用BeanPostProcessor自动实现它。首先定义用于注入新委托实现的扩展接口:

public interface Wrapper extends MyInterface {
    void setTarget(MyInterface target);
}

然后创建BeanPostProcessor,它将MyInterface的所有实现包装到CGLIB代理。代理充当MyClass(使其能够注入MyClass类型的字段)和Wrapper(使其能够更改目标)。代理将所有原始调用重定向到MyClass目标(最初设置为Spring中声明的值),调用Wrapper.setTarget会导致目标更改。

@Component
public static class MyPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Object result = bean;
        if (bean instanceof MyInterface) {
            final MyInterface myInterface = (MyInterface)bean;
            Class<? extends MyInterface> clazz = myInterface.getClass();
            if (!isFinal(clazz.getModifiers())) {
                result = Enhancer.create(clazz, new Class[] {MyInterface.class}, new MethodInterceptor() {
                    private MyInterface target = myInterface;
                    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                        if (method.getName().equals("setTarget") && method.getDeclaringClass().equals(Wrapper.class) && method.getParameterCount() == 1 && method.getParameterTypes()[0].equals(MyInterface.class)) {
                            this.target = (MyInterface)args[0];
                            return null;
                        } else {
                            Object result = proxy.invoke(this.target, args);
                            return result;
                        }
                    }
                });
            }
        }
        return result;
    }

}

简单的想法是:在Spring中定义bean,因为它是普通的bean,在初始化后调整它。

答案 4 :(得分:2)

在创建之前有一些方法可以操作spring上下文。

  1. 一种方法是,使用GenericApplicationContext和GenericBeanDefinition类来操作上下文。以下示例代码显示了此解决方案:

    GenericApplicationContext context = new GenericApplicationContext();
    
    XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(context);
    xmlReader.loadBeanDefinitions(new ClassPathResource(original-context));
    BeanDefinitionRegistry registry = ((BeanDefinitionRegistry) context);
    
    GenericBeanDefinition myBean = new GenericBeanDefinition();
    myBean.setBeanClass(MyCustomClass.class);
    myBean.getPropertyValues().add("name", "My-Name");
    registry.registerBeanDefinition("my_bean_name", myBean);
    
    context.refresh();
    
  2. 通过此代码段,您可以添加或删除或更改创建的bean。

    1. 第二个解决方案是在Spring中使用BeanPostProcessor机制。对于详细信息,请参阅此网址:http://www.roseindia.net/tutorial/spring/spring3/ioc/beanpostprocessor.htmlWhy is this BeanPostProcessor needed in addition to a UserDetailsService in this Spring 3.0 authentication example?

答案 5 :(得分:0)

你在这里划了一条罚款线。基本上您正在尝试将应用程序逻辑放在Spring容器中。避免使用配置文件进行编程,并仅使用Spring(或任何DI框架)进行基本连线。

代理建议@mrembisz make是优选的。这样,应用程序逻辑和配置就分开了。

答案 6 :(得分:0)

另一种简单的方法是将类的Atomic Reference用作Bean,而不是直接使用类。然后,您可以将Atomic Reference注入到任何地方并进行更新。在原子引用中对其进行更新后,所有其他服务都将使用该类的最新版本。而且我认为这是有道理的。

请阅读以下内容以获取原子引用用法:https://stackoverflow.com/a/24765321/5197662