假设我在Spring容器中定义了一个bean(例如BeanA),并将此bean注入到一个对象中。 (例如BeanAUser)
在运行期间,我可以使用另一个bean实例来替换spring容器内的原始BeanA吗?并且还将这个新的bean实例重新注入BeanAUser以替换原来的BeanA?
答案 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
参数表示以下内容:
所以我想你的课看起来像下面的
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上下文。
一种方法是,使用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。
答案 5 :(得分:0)
你在这里划了一条罚款线。基本上您正在尝试将应用程序逻辑放在Spring容器中。避免使用配置文件进行编程,并仅使用Spring(或任何DI框架)进行基本连线。
代理建议@mrembisz make是优选的。这样,应用程序逻辑和配置就分开了。
答案 6 :(得分:0)
另一种简单的方法是将类的Atomic Reference用作Bean,而不是直接使用类。然后,您可以将Atomic Reference注入到任何地方并进行更新。在原子引用中对其进行更新后,所有其他服务都将使用该类的最新版本。而且我认为这是有道理的。
请阅读以下内容以获取原子引用用法:https://stackoverflow.com/a/24765321/5197662