Suppose I define a bean (eg BeanA) inside the Spring container, and this bean is injected into an object. (eg BeanAUser)
During run-time, can I use another bean instance to replace the original BeanA inside the spring container?? And also re-injects this new bean instance into BeanAUser in order to replace the original BeanA?
2.4. @PreDestroy annotated method is invoked just before the bean is about be destroyed inside bean container.
And finally, the bean is destroyed when the spring container is closed. Therefore, if we want to execute some code on the bean instantiation and just after closing the spring container, then we can write that code inside the custom init() method and the destroy() method.
Spring provides the following two callback interfaces: InitializingBean : Declares the afterPropertiesSet() method which can be used to write the initialization logic. The container calls the method after properties are set. DisposableBean : Declares the destroy() method which can be used to write any clean up code.
It can be easily achieved using a proxy. Create a delegating implementation of your interface and switch object it is delegating to.
@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(..);
}
..
}
Spring introduced the new RefreshScope to replace a bean at runtime. Internally, a proxy is created as described in the answer of mrembisz.
@RefreshScope
@Component
public class MyBean { ... }
The way I would do this is by using a system called arbitrary-method-replacement.
Create a class that implements org.springframework.beans.factory.support.MethodReplacer
, this will force you to create a method like so
public Object reimplement(Object o, Method m, Object[] args) throws Throwable
The parameters mean the following:
So I would imagine your class to look something like the following
public BeanAUserHelper implements MethodReplacer {
public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
if (some expression){
return beanA;
}
else {
return beanB;
}
}
}
In your bean configuration, you then instruct Spring to replace the getBeanX()
method on your BeanAUser
like so
<!-- 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"/>
I hope I understood your problem correctly :)
Assuming MyClass
in mrembisz's answer is not final, one doesn't have to implement decorator pattern manually and can implement it automatically using BeanPostProcessor
. First define extension interface for injecting new delegating implementation:
public interface Wrapper extends MyInterface {
void setTarget(MyInterface target);
}
Then create BeanPostProcessor
which wraps all implementations of MyInterface
to CGLIB proxy. Proxy acts as both MyClass
(which enables it to be injected into fields of MyClass
type) and Wrapper
(which enables it to change target). Proxy redirects all original invocations to MyClass
target (which is initially set to value declared in Spring), invocation of Wrapper.setTarget
results in target change.
@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;
}
}
Simply the idea is: define bean in Spring as it was a normal bean, tweak it after initialization.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With