Sayak Mukhopadhyay
Sayak Mukhopadhyay

Reputation: 1474

Alternative to ApplicationContext.getBean() in Spring

I am using SpringBoot in my application and am currently using applicationContext.getBean(beanName,beanClass) to get my bean before performing operations on it. I saw in a couple of questions that it is discouraged to use getBean(). Since I am very new to Spring I don't know all the best practices and am conflicted. The solutions posed in the above linked question probably won't work in my use case. How should I approach this?

@RestController
@RequestMapping("/api")
public class APIHandler {
    @Value("${fromConfig}")
    String fromConfig;

    private ApplicationContext applicationContext;

    public Bot(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @PostMapping(value = "")
    public ResponseEntity post(@RequestBody HandlingClass requestBody) {
        SomeInterface someInterface = applicationContext.getBean(fromConfig, SomeInterface.class);
        someInterface.doSomething();
    }
}

I have an interface called SomeInterface defined like:

public interface SomeInterface {

    void doSomething();
}

And I have 2 classes which implements this interface called UseClass1 and UseClass2. My config file stores a string with the bean name of a class which I need to know in run-time and call the appropriate implementation of the method.

Any directions would be appreciated.

Upvotes: 4

Views: 15239

Answers (5)

murat karakas
murat karakas

Reputation: 114

You can define your spring bean component with

@Profile("dev") , @Profile("test")

and inject as mention comment, then switch profile with

-Dspring.profiles.active=test   jvm argument

Upvotes: 2

Okoch
Okoch

Reputation: 465

I think you should probably use a configuration class to produce your bean based on the fromConfig string value:

Your controller:

@RestController
@RequestMapping("/api")
public class APIHandler {

    @Autowired
    SomeInterface someInterface;

    @PostMapping(value = "")
    public ResponseEntity post(@RequestBody HandlingClass requestBody) {
        someInterface.doSomething();
    }
}

The bean producer:

@Configuration
public class SomeInterfaceProducer {

    @Value("${fromConfig}")
    String fromConfig;

    @Bean
    public SomeInterface produce() {

        if (fromConfig.equals("aValueForUseClass1") {
            return new UseClass1();
        } else {
            return new UseClass2();
        }
        //...
    }
}

or if you have DI in UseClass1 and/or UseClass2:

@Configuration
public class SomeInterfaceProducer {

    @Value("${fromConfig}")
    String fromConfig;

    @Bean
    public SomeInterface produce(@Autowired YourComponent yourComponent) {
        SomeInterface someInterface;
        if (fromConfig.equals("aValueForUseClass1") {
            someInterface = new UseClass1();
            someInterface.setYourComponent(yourComponent);
            // or directly with the constructor if you have one with yourComponent as parameter.
        } else {
            someInterface = new UseClass2();
            someInterface.setYourComponent(yourComponent);
        }
        //...
    }
}

Upvotes: 2

Nikolai  Shevchenko
Nikolai Shevchenko

Reputation: 7521

Since Spring 4.3 you can autowire all implementations into a Map consisting of pairs beanName <=> beanInstance:

public class APIHandler {

    @Autowired
    private Map<String, SomeInterface> impls;

    public ResponseEntity post(@RequestBody HandlingClass requestBody) {
        String beanName = "..."; // resolve from your requestBody        
        SomeInterface someInterface = impls.get(beanName);
        someInterface.doSomething();
    }
}

assuming you have two implementations like following

// qualifier can be omitted, then it will be "UseClass1" by default
@Service("beanName1") 
public class UseClass1 implements SomeInterface { } 

// qualifier can be omitted, then it will be "UseClass2" by default
@Service("beanName2")
public class UseClass2 implements SomeInterface { } 

Upvotes: 6

Ramesh Fadatare
Ramesh Fadatare

Reputation: 589

This is only code works for me to get beans dynamically from ApplicationContext

@Service
public class AuthenticationService {

    @Autowired
    private ApplicationContext сontext;

    public boolean authenticate(...) { 
        boolean useDb = ...;   //got from db
        IAuthentication auth = context.getBean(useDb ? DbAuthentication.class : LdapAuthentication.class);       
        return auth.authenticate(...);
    }
}

Upvotes: 2

Bal&#225;zs N&#233;meth
Bal&#225;zs N&#233;meth

Reputation: 6657

The real question is not how to solve this, but why would you inject something different based on a configuration value?

If the answer is testing, then perhaps it's better to use @Profiles as @murat suggested.

Why are different implementations of an interface there on your classpath?

Can't you package your application in a way that only one is there for one use case? (see ContextConfiguration)

Upvotes: 1

Related Questions