Inject spring bean dynamically
Asked Answered
C

8

36

In a java-spring web-app I would like to be able to dynamically inject beans. For example I have an interface with 2 different implementations:

enter image description here

In my app I'm using some properties file to configure injections:

#Determines the interface type the app uses. Possible values: implA, implB
myinterface.type=implA

My injections actually loaded conditionally relaying on the properties values in the properties file. For example in this case myinterface.type=implA wherever I inject MyInterface the implementation that will be injected will be ImplA (I accomplished that by extending the Conditional annotation).

I would like that during runtime - once the properties are changed the following will happen (without server restart):

  1. The right implementation will be injected. For example when setting myinterface.type=implB ImplB will be injected where-ever MyInterface is used
  2. Spring Environment should be refreshed with the new values and re-injected as well to beans.

I thought of refreshing my context but that creates problems. I thought maybe to use setters for injection and re-use those setters once properties are re-configured. Is there a working practice for such a requirement?

Any ideas?

UPDATE

As some suggested I can use a factory/registry that holds both implementations (ImplA and ImplB) and returns the right one by querying the relevant property. If I do that I still have the second challenge - the environment. for example if my registry looks like this:

@Service
public class MyRegistry {

private String configurationValue;
private final MyInterface implA;
private final MyInterface implB;

@Inject
public MyRegistry(Environmant env, MyInterface implA, MyInterface ImplB) {
        this.implA = implA;
        this.implB = implB;
        this.configurationValue = env.getProperty("myinterface.type");
}

public MyInterface getMyInterface() {
        switch(configurationValue) {
        case "implA":
                return implA;
        case "implB":
                return implB;
        }
}
}

Once property has changed I should re-inject my environment. any suggestions for that?

I know I can query that env inside the method instead of constructor but this is a performance reduction and also I would like to think of an ider for re-injecting environment (again, maybe using a setter injection?).

Classified answered 18/10, 2016 at 9:53 Comment(12)
how will change the value in properties to myinterface.type=implB, and how will the system to get know you done/finished changed a valueMuskogean
Also how application should figure out case when already injected dependency in use and you force swap of implementation? Also what is the main aim of this approach?Methoxychlor
@Muskogean Assume that I know that (for example using a WatchService), this is not the challenge here.Classified
@Methoxychlor - My question is how to do the swap. The main purpose is to be able to change application behavior in runtime based on configuration. Imagine that ImplA and ImplB are running 2 different calculation algorithms, when the app starts I use ImplA algorithm and after a while would like to use algorithm of ImplB (meaning to inject ImplB instead of ImplA).Classified
I don't know why, but your problem sounds more like a factory design to me. I might just go ahead and create a factory and handle it there instead of complex dynamic switch of interface runtime. I am not sure about your problem, so there is a possibility that I might be getting it wrong.Spraggins
I agreed with Harry. Imagine that automobile factory should change personal only for production of a little bit changed automobiles.Methoxychlor
@HarryJoy I agree this could be solved by injecting a factory while this factory will query for the property value and return the right implementation. But this means I need to repeatedly query this property for each request. Maybe that is not so bad, but I would like to see if we can come up with other ideas.Classified
@forhas, please check this link: #12801269. It might be helpful for you. But its really dark way swap bean implementation in runtime. Your problem should be resolved by correct design pattern.Methoxychlor
@Methoxychlor Thanks! I did try to play around quite a lot with closing/refreshing context, also recreating a new context.. It led to weird problems, also I'm not sure how the application reacts during context close (requests are probably refused).Classified
@forhas, there is good answer on similar question: #21221625Methoxychlor
@Methoxychlor Indeed this looks like a very detailed answer, I will have a look and give it a shot.Classified
have you tried spring-plugin? It markets itself as "OSGi lite"Remarkable
A
32

I would keep this task as simple as possible. Instead of conditionally load one implementation of the MyInterface interface at startup and then fire an event that triggers dynamic loading of another implementation of the same interface, I would tackle this problem in a different way, that is much simpler to implement and maintain.

First of all, I'd just load all possible implementations:

@Component
public class MyInterfaceImplementationsHolder {

    @Autowired
    private Map<String, MyInterface> implementations;

    public MyInterface get(String impl) {
        return this.implementations.get(impl);
    }
}

This bean is just a holder for all implementations of the MyInterface interface. Nothing magic here, just common Spring autowiring behavior.

Now, wherever you need to inject a specific implementation of MyInterface, you could do it with the help of an interface:

public interface MyInterfaceReloader {

    void changeImplementation(MyInterface impl);
}

Then, for every class that needs to be notified of a change of the implementation, just make it implement the MyInterfaceReloader interface. For instance:

@Component
public class SomeBean implements MyInterfaceReloader {

    // Do not autowire
    private MyInterface myInterface;

    @Override
    public void changeImplementation(MyInterface impl) {
        this.myInterface = impl;
    }
}

Finally, you need a bean that actually changes the implementation in every bean that has MyInterface as an attribute:

@Component
public class MyInterfaceImplementationUpdater {

    @Autowired
    private Map<String, MyInterfaceReloader> reloaders;

    @Autowired
    private MyInterfaceImplementationsHolder holder;

    public void updateImplementations(String implBeanName) {
        this.reloaders.forEach((k, v) -> 
            v.changeImplementation(this.holder.get(implBeanName)));
    }
}

This simply autowires all beans that implement the MyInterfaceReloader interface and updates each one of them with the new implementation, which is retrieved from the holder and passed as an argument. Again, common Spring autowiring rules.

Whenever you want the implementation to be changed, you should just invoke the updateImplementations method with the name of the bean of the new implementation, which is the lower camel case simple name of the class, i.e. myImplA or myImplB for classes MyImplA and MyImplB.

You should also invoke this method at startup, so that an initial implementation is set on every bean that implements the MyInterfaceReloader interface.

Abmho answered 2/11, 2016 at 2:48 Comment(4)
Seems that this is an interesting solution. It does not use any "Spring magic", some might like this. As for me I like Spring and the solution with proxy uses all the power of "magic".Dewaynedewberry
I won't use this approach, because it clutters every client with the concept of implementation switching. This is exactly the use case of a dynamic proxy. So why not use it :-)Associative
@JohannesLeimer Well, maybe clutter is a little too much saying, isn't it? Clients only have to implement the MyInterfaceReloader interface, which only sets an attribute. Dynamic proxy is a very good approach, though, I agree with you on this.Abmho
We need people who thinks on their own way, instead just mugging it up on how to use existing library? Thanks! Federico. At least i might learn what is Dynamic proxy implementation looks like with fewer code samples here.Magel
N
16

I solved a similar issue by using org.apache.commons.configuration.PropertiesConfiguration and org.springframework.beans.factory.config.ServiceLocatorFactoryBean:

Let VehicleRepairService be an interface:

public interface VehicleRepairService {
    void repair();
}

and CarRepairService and TruckRepairService two classes that implements it:

public class CarRepairService implements VehicleRepairService {
    @Override
    public void repair() {
        System.out.println("repair a car");
    }
}

public class TruckRepairService implements VehicleRepairService {
    @Override
    public void repair() {
        System.out.println("repair a truck");
    }
}

I create an interface for a service factory:

public interface VehicleRepairServiceFactory {
    VehicleRepairService getRepairService(String serviceType);
}

Let use Config as configuration class:

@Configuration()
@ComponentScan(basePackages = "config.test")
public class Config {
    @Bean 
    public PropertiesConfiguration configuration(){
        try {
            PropertiesConfiguration configuration = new PropertiesConfiguration("example.properties");
            configuration
                    .setReloadingStrategy(new FileChangedReloadingStrategy());
            return configuration;
        } catch (ConfigurationException e) {
            throw new IllegalStateException(e);
        }
    }

    @Bean
    public ServiceLocatorFactoryBean serviceLocatorFactoryBean() {
        ServiceLocatorFactoryBean serviceLocatorFactoryBean = new ServiceLocatorFactoryBean();
        serviceLocatorFactoryBean
                .setServiceLocatorInterface(VehicleRepairServiceFactory.class);
        return serviceLocatorFactoryBean;
    }

    @Bean
    public CarRepairService carRepairService() {
        return new CarRepairService();
    }

    @Bean
    public TruckRepairService truckRepairService() {
        return new TruckRepairService();
    }

    @Bean
    public SomeService someService(){
        return new SomeService();
    }
}

By using FileChangedReloadingStrategy your configuration be reload when you change the property file.

service=truckRepairService
#service=carRepairService

Having the configuration and the factory in your service, let you can get the appropriate service from the factory using the current value of the property.

@Service
public class SomeService  {

    @Autowired
    private VehicleRepairServiceFactory factory;

    @Autowired 
    private PropertiesConfiguration configuration;


    public void doSomething() {
        String service = configuration.getString("service");

        VehicleRepairService vehicleRepairService = factory.getRepairService(service);
        vehicleRepairService.repair();
    }
}

Hope it helps.

Nahuatlan answered 2/11, 2016 at 10:30 Comment(0)
D
9

If I understand you correctly then the goal is not to replace injected object instances but to use different implementations during interface method call depends on some condition at run time.

If it is so then you can try to look at the Sring TargetSource mechanism in combination with ProxyFactoryBean. The point is that proxy objects will be injected to beans that uses your interface, and all the interface method calls will be sent to TargetSource target.

Let's call this "Polymorphic Proxy".

Have a look at example below:

ConditionalTargetSource.java

@Component
public class ConditionalTargetSource implements TargetSource {

    @Autowired
    private MyRegistry registry;

    @Override
    public Class<?> getTargetClass() {
        return MyInterface.class;
    }

    @Override
    public boolean isStatic() {
        return false;
    }

    @Override
    public Object getTarget() throws Exception {
        return registry.getMyInterface();
    }

    @Override
    public void releaseTarget(Object target) throws Exception {
        //Do some staff here if you want to release something related to interface instances that was created with MyRegistry.
    }

}

applicationContext.xml

<bean id="myInterfaceFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="proxyInterfaces" value="MyInterface"/>
    <property name="targetSource" ref="conditionalTargetSource"/>
</bean>
<bean name="conditionalTargetSource" class="ConditionalTargetSource"/>

SomeService.java

@Service
public class SomeService {

  @Autowired
  private MyInterface myInterfaceBean;

  public void foo(){
      //Here we have `myInterfaceBean` proxy that will do `conditionalTargetSource.getTarget().bar()`
      myInterfaceBean.bar();
  }

}

Also if you want to have both MyInterface implementations to be Spring beans, and the Spring context could not contains both instances at the same time then you can try to use ServiceLocatorFactoryBean with prototype target beans scope and Conditional annotation on target implementation classes. This approach can be used instead of MyRegistry.

P.S. Probably Application Context refresh operation also can do what you want but it can cause other problems such as performance overheads.

Dewaynedewberry answered 19/10, 2016 at 3:39 Comment(0)
A
5

This may be a duplicate question or at least very similar, anyway I answered this sort of question here: Spring bean partial autowire prototype constructor

Pretty much when you want a different beans for a dependency at run-time you need to use a prototype scope. Then you can use a configuration to return different implementations of the prototype bean. You will need to handle the logic on which implementation to return yourself, (they could even be returning 2 different singleton beans it doesn't matter) But say you want new beans, and the logic for returning the implementation is in a bean called SomeBeanWithLogic.isSomeBooleanExpression(), then you can make a configuration:

@Configuration
public class SpringConfiguration
{

    @Bean
    @Autowired
    @Scope("prototype")
    public MyInterface createBean(SomeBeanWithLogic someBeanWithLogic )
    {
        if (someBeanWithLogic .isSomeBooleanExpression())
        {
            return new ImplA(); // I could be a singleton bean
        }
        else
        {
            return new ImplB();  // I could also be a singleton bean
        }
    }
}

There should never be a need to reload the context. If for instance, you want the implementation of a bean to change at run-time, use the above. If you really need to reload your application, because this bean was used in constructors of a singleton bean or something weird, then you need to re-think your design, and if these beans are really singleton beans. You shouldn't be reloading the context to re-create singleton beans to achieve different run-time behavior, that is not needed.

Edit The first part of this answer answered the question about dynamically injecting beans. As asked, but I think the question is more of one: 'how can I change the implementation of a singleton bean at run-time'. This could be done with a proxy design pattern.

interface MyInterface 
{
    public String doStuff();
}

@Component
public class Bean implements MyInterface
{
    boolean todo = false; // change me as needed

    // autowire implementations or create instances within this class as needed
    @Qualifier("implA")
    @Autowired
    MyInterface implA;

    @Qualifier("implB")
    @Autowired
    MyInterface implB;

    public String doStuff()
    {
        if (todo)
        {
            return implA.doStuff();
        }
        else
        {
            return implB.doStuff();
        }
    }   
}
Anglocatholic answered 1/11, 2016 at 23:7 Comment(8)
you are right about the unnecessary overhead with the application context reloading but the issue with dynamic behaviour change of the injected bean is absolutely legal and that kind of design called Polymorphism in OOP. With regard to your answer then it is very similar to the @Conditional annotation proposed in another answer below.Dewaynedewberry
@SergeyBespalov No, polymorphism is not about a particular object behaving differently at run-time. An object will only behave differently because it's state has changed. Furthermore my answer is not similar to below because the context does not need to be reloaded. Whether some reference has changed from one implementation to another you might talk about polymorphism there, but we are not. We are talking about Spring and dependency injection.Anglocatholic
What I am saying is that a singleton bean should not be recreated. If for some crazy reason you want to recreate a singleton bean, it should rather just have prototype scope, and your configuration should handle which implementation is given. If you want to change a singleton bean's implementation at run-time, then change the state, do not recreate it.Anglocatholic
Polymorphism is exactly about this, and I don't see any State in here.Dewaynedewberry
No it's not. It is about multiple classes satisfying an interface. One particular object (instance) will only change it's behavior if it's state has changed. As an example Bean.doStuff(), will only be different if todo has changed, or the data in implA or implB has changed. MyInterface can have different implementations and that is because of polymorphism. But doStuff changes because the state has changed. You are confused.Anglocatholic
I expressed my point of view, let the community decide which answers are good. @Classified will you join to vote?Dewaynedewberry
I can't see the benefit of recreating my beans every-time they are required or injected. These are singleton services that might hold a state which I'm using in my app. I think that by using a condition (if statement) to decide which bean/operation to use you are missing what OOP is aiming for.Classified
The if statement is there only to represent some logic as to which implementation to choose.Anglocatholic
P
2

You can use @Resource annotation for injection as originally answered here

e.g.

@Component("implA")
public class ImplA implements MyInterface {
  ...
}
@Component("implB")
public class ImplB implements MyInterface {
  ...
}
@Component
public class DependentClass {

  @Resource(name = "\${myinterface.type}") 
  private MyInterface impl;

}

and then set the implementation type in properties file as -

myinterface.type=implA
Photo answered 1/2, 2020 at 11:52 Comment(0)
R
1

Be aware that - if interesting to know about - FileChangedReloadingStrategy makes your project highly dependent on the deployment conditions: the WAR/EAR should be exploded by container and your should have direct access to the file system, conditions that are not always met in all situations and environments.

Rimma answered 5/11, 2016 at 8:5 Comment(0)
E
0

You can use Spring @Conditional on a property value. Give both Beans the same name and it should work as only one Instance will be created.

Have a look here on how to use @Conditional on Services and Components: http://blog.codeleak.pl/2015/11/how-to-register-components-using.html

Efficacy answered 18/10, 2016 at 11:39 Comment(3)
Ok, and does this resolve my requirement to change injected element during runtime?Classified
Well I think you would probably have to reload the context with this.Efficacy
Now you'r talking.Classified
C
0
public abstract class SystemService {

}

public class FooSystemService extends FileSystemService {

}

public class GoSystemService extends FileSystemService {

} 


@Configuration
public class SystemServiceConf {


    @Bean
    @Conditional(SystemServiceCondition.class)
    public SystemService systemService(@Value("${value.key}") value) {
        switch (value) {
            case A:
                return new FooSystemService();
            case B:
                return new GoSystemService();
            default:
                throw new RuntimeException("unknown value ");
        }
    }
}

public class SystemServiceCondition implements Condition {


    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        return true;
    }
}
Chesney answered 31/3, 2022 at 4:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.