Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 346 Vote(s) - 3.49 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Replace a bean inside the spring container during run-time

#1
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?
Reply

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

..
}
Reply

#3
There are ways for manipulating spring context before that created.

1. A way is, you use GenericApplicationContext and GenericBeanDefinition classes for manipulate context. Following sample code shown this solution :

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();

by this snippet code you can add or remove or change beans befor it created.

2. Second solution is using BeanPostProcessor mechanism in spring. For detailes see this url :

[To see links please register here]

or

[To see links please register here]


Reply

#4
You're crossing a fine line here. Basically you are trying to put application logic in the Spring container. Avoid programming with your configuration files and use Spring (or whatever DI framework) only for basic wiring.

The proxy suggestion @mrembisz makes is therefor the preferred one. That way application logic and configuration are separated.
Reply

#5
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:

- o - the bean instance you're replacing a method on
- m - the method meta we are replacing
- args - the method arguments supplied (if any)

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 :)
Reply

#6
Spring introduced the new [RefreshScope][1] to replace a bean at runtime. Internally, a proxy is created as described in the [answer of mrembisz][2].

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

[1]:

[To see links please register here]

"Refresh Scope"
[2]:

[To see links please register here]

Reply

#7
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.
Reply

#8
Another easy approach can be using Atomic Reference of your class as a Bean instead of using the class directly. Then you can inject that Atomic Reference anywhere and update it. All other services will use the latest version of your class after you update it in atomic reference. And I think it makes sense.

Read this for atomic reference usage:

[To see links please register here]


One disadvantage of this approach is loosing that spring can not process annotations of your class. For example, u cant use `@Cachable`, `@Async`, `@PreDestroy` and others. But the solution is good when u just need an object and it properties.
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through