ISlimani
ISlimani

Reputation: 1673

How to choose bean implementation at runtime for every http request

I am having two implementations of my component.

public interface MyComponent {

}

imple1

@Component("impCompf")
@Lazy
@RequestScope
public class ImpComp1 implements MyComponent {

}

imple2

@Component("impComps")
@Lazy
@RequestScope
public class ImpComp2 implements MyComponent {

}

What I did so far is to create two conditions like so:

imple1

public class FirstCondition implements Condition {

        @Override
    public boolean matches(ConditionContext arg0, AnnotatedTypeMetadata arg1) {        

        return staticVariable.contains("impCompf"); 
    }
}

Same goes for imple2

and define a configuration class

@Configuration
public class MyConfiguration {
    @Bean
    @Conditional(FirstCondition .class)
    @Primary    
    public MyComponent getComp1() {
       return new ImpComp1();
    }

public static String staticVariable= "impCompf";

and in My main controller:

@RequestMapping(value="api/{co}", method=RequestMethod.POST)
public ResponseEntity<Modelx> postSe(@PathVariable("co") String co) {
    if(co.contains("impCompf"))
        staticVariable = "impCompf";
    else (co.contains("impComps"))
        staticVariable = "impComps";

What I want: for every http request I want to load proper implementation

But however what I am getting is the implementation defined first in the static variable.

If is there another elegant and better way, i'd like to know about it.

Upvotes: 2

Views: 3308

Answers (1)

David
David

Reputation: 8196

I think there is some confusion here about the purpose of the conditions. These aren't being used at the time your requests arrive to autowire the candidate bean into your controller. These are being used when the application is started to configure the application context based on the environment and classpath etc...

There is no need for the conditional classes that you have created. This is defining the configuration of the beans when the context starts and not on a per request basis at runtime.

The use of the static variable is also problematic is a scenario with one or more concurrent requests or in a case where multiple threads may observe different values unless some other mechanism in the java memory model is being used (such as volatile or establishing a happens before relationship, e.g. with sychnronized)

There are a number of ways to do what you appear to be trying to achieve. Since ultimately, you appear to be using a path parameter supplied by a client to determine which service you want to invoke you could use a classic factory pattern to return the correct interface implementation based on the string input programmatically.

Alternatively you could create two distinct controller methods which are distinguished by a query parameter or endpoint name or path match etc. You could then have the appropriate service injected by a qualified bean name

Although perhaps generally recommended, you could also inject an application context instance and search the it looking for the relevant bean by name or class: https://brunozambiazi.wordpress.com/2016/01/16/getting-spring-beans-programmatically/ - although This is more cumbersome and you'd need to handle things like org.springframework.beans.factory.NoSuchBeanDefinitionException or casting in some cases - best avoided in favour of one of the other methods.

Upvotes: 3

Related Questions