Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I select Spring bean instance at runtime

Tags:

java

spring

Based on parameters passed to a method, I need to select from one of many Spring beans that are implementations of the same class, but configured with different parameters.

E.g. if user A invokes the method, I need to call dooFoo() on bean A, but if it's user B then I need to call the very same method, only on bean B.

Is there a 'Springier' way of doing this other than sticking all the beans in a map, and deriving a key from the parameters passed to my method?

like image 271
EngineerBetter_DJ Avatar asked Jul 17 '13 14:07

EngineerBetter_DJ


People also ask

How can I get specific beans in Spring boot?

Ways to get loaded beans in Spring / Spring boot ApplicationContext. getBeanDefinitionNames() will return names of beans which is correctly loaded. getBean(String name) method using that we can get particular bean using bean name. * Created by JavaDeveloperZone on 07-13-2019.

When Spring has a new bean instance each time?

For example, to force Spring to produce a new bean instance each time one is needed, you should declare the bean's scope attribute to be prototype. Similarly, if you want Spring to return the same bean instance each time one is needed, you should declare the bean's scope attribute to be singleton.

How does Spring know which bean to use?

The identifier of the parameter matches the name of one of the beans from the context (which is the same as the name of the method annotated with @Bean that returns its value). In this case, Spring chooses that bean for which the name is the same as the parameter.


2 Answers

We face that issue in our project, and we solve it through a Factory-Like class. The client class -the one that needed the bean at runtime- had an instance of the factory, that was injected through Spring:

@Component
public class ImTheClient{

    @Autowired
    private ImTheFactory factory;

    public void doSomething(
            Parameters parameters) throws Exception{        
        IWantThis theInstance = factory.getInstance(parameters);        

    }

}

So, the IWantThis instance depends on the runtime value of the parameters parameter. The Factory implementation goes like this:

@Component
public class ImTheFactoryImpl implements
        ImTheFactory {

    @Autowired
    private IWantThisBadly anInstance;
    @Autowired
    private IAlsoWantThis anotherInstance;

    @Override
    public IWantThis getInstance(Parameters parameters) {
        if (parameters.equals(Parameters.THIS)) {
            return anInstance;
        }

        if (parameters.equals(Parameters.THAT)) {
            return anotherInstance;
        }

        return null;
    }
}

So, the factory instance holds reference to both of the posible values of the IWantThis class, being IWantThisBadly and IAlsoWantThis both implementations of IWantThis.

like image 78
Carlos Gavidia-Calderon Avatar answered Oct 13 '22 15:10

Carlos Gavidia-Calderon


Seems like do you want a ServiceLocator using the application context as registry.

See ServiceLocatorFactoryBean support class for creating ServiceLocators mapping keys to bean names without coupling client code to Spring.

Other option is to use a naming convention or annotation based configuration.

for example, assuming that you annotate Services with @ExampleAnnotation("someId"), you can use something like the following Service Locator to retrieve them.

public class AnnotationServiceLocator implements ServiceLocator {

    @Autowired
    private ApplicationContext context;
    private Map<String, Service> services;

    public Service getService(String id) {
        checkServices();
        return services.get(id);
    }

    private void checkServices() {
        if (services == null) {
            services = new HashMap<String, Service>();
            Map<String, Object> beans = context.getBeansWithAnnotation(ExampleAnnotation.class);
            for (Object bean : beans.values()) {
                ExampleAnnotation ann = bean.getClass().getAnnotation(ExampleAnnotation.class);
                services.put(ann.value(), (Service) bean);
            }
        }
    }   
}
like image 33
Jose Luis Martin Avatar answered Oct 13 '22 17:10

Jose Luis Martin