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

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

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

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

@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(..); } .. } 

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

创建一个实现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()上的getBeanX()方法

        

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

Spring引入了新的RefreshScope来在运行时替换bean。 在内部,如mrembisz的答案中所述创建代理。

 @RefreshScope @Component public class MyBean { ... } 

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

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

然后创建BeanPostProcessorMyInterface所有实现包装到CGLIB代理。 Proxy充当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 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,在初始化后调整它。

有一些方法可以在创建之前操纵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(); 

通过此代码段,您可以添加或删除或更改它创建的bean。

  1. 第二种解决方案是在Spring中使用BeanPostProcessor机制。 对于详细信息,请参阅此URL: http ://www.roseindia.net/tutorial/spring/spring3/ioc/beanpostprocessor.html或者为什么在此Spring 3.0身份validation示例中除了UserDetailsS​​ervice之外还需要此BeanPostProcessor?

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

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