Reputation: 1474
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
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
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
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
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
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 @Profile
s 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