我有一个接口的两个实现,我想根据配置选择使用哪个实现。限定符解决方案由于在配置之前初始化而无法使用。我该怎么做?
我有一个接口的两个实现,我想根据配置选择使用哪个实现。限定符解决方案由于在配置之前初始化而无法使用。我该怎么做?
我已经收到您的评论:
我有两种不同的任务实现方式,每个月可以更改类型,因此如果可配置,则可以减少部署和代码更改。
您可能会像这样:
interface Job {
void foo();
}
class JobA implements Job {
void foo() {...}
}
class JobB implements Job {
void foo() {...}
}
class JobExecutor {
Job job;
// autowired constructor
public JobExecutor(Job job) {this.job = job;}
}
如果我理解正确的话,同时在同一应用程序上下文中加载两个bean是没有意义的。
但如果是这样的话,那么@Qualifier
不是合适的工具。
我建议使用集成到Spring Boot中的条件来解决:
@Configuration
public class MyConfiguration {
@ConditionalOnProperty(name = "job.name", havingValue = "jobA")
@Bean
public Job jobA() {
return new JobA();
}
@ConditionalOnProperty(name = "job.name", havingValue = "jobB")
@Bean
public Job jobB() {
return new JobB();
}
@Bean
public JobExecutor jobExecutor(Job job) {
return new JobExecutor(job);
}
}
现在,在application.properties
(或yaml,无论你有哪种)中定义:
job.name = jobA # or jobB
当然,您可以使用更符合您业务领域的、更容易理解的名称,而不是jobA/jobB
。
如果您在Spring基于Java的配置上稍微调整一下,就可以做到这一点,其中您可以根据配置值以编程方式决定正确的实现:
@Configuration
public class MyAppContext implements EnvironmentAware{
private Environment env;
@Override
public void setEnvironment(final Environment env) {
this.env = env;
}
@Bean
public MyBeanByConfig myBeanByConfig(){
String configValue = env.getProperty("mybean.config");
if(configValue.equals("1")){
return new MyBeanByConfigOne();
}else{
return new MyBeanByConfigTwo();
}
}
}
在限定符上,您需要放置:
@Qualifier("myBeanByConfig")
您可能需要在配置类上添加@ComponentScan
和@PropertySource
。
public interface Foo {
void doSomething();
}
还有两种实现方式:
public class Foo_A implements Foo {
@Override
doSomething() {...}
}
public class Foo_B implements Foo {
@Override
doSomething() {...}
}
现在你想根据属性文件中的属性值使用Foo_A/Foo_B:
foo_name: "A"
我发现最简单的方法是:
@Component("Foo_A")
public class Foo_A implements Foo {
@Override
doSomething() {...}
}
@Component("Foo_B")
public class Foo_B implements Foo {
@Override
doSomething() {...}
}
public class Bar {
@Value("${foo_name}")
private String fooName;
@Qualifier("Foo_A")
private Foo fooA;
@Qualifier("Foo_B")
private Foo fooB;
public void doSomething() {
if (fooName.equals("A")) {
fooA.doSomething();
} else {
fooB.doSomething();
}
}
}
我最终将两个自动装配的实现添加到主应用程序类中,然后为每个定义一个bean:
@Autowired
TypeOneImpl typeOneImpl
@Bean(name = "typeOneImpl")
public InterfaceRClass getTypeOneImpl()
{
return typeOneImpl;
}
@Value("${myClass.type}")
private String configClassType;
// the below should be defined in constructor
private final InterfaceRClass interfaceRClassElement ;
并使用@Autowired注解为其添加了一个setter方法:
@Autowired
public void setMyClassType(ApplicationContext context) {
interfaceRClassElement = (InterfaceRClass) context.getBean(configClassType);
}
在配置中,该值应该是typeOneImpl(typeTwoImpl是用于附加实现的)
知道通过构造函数IOC工作正常,也许我会这样做:
@Component
public class JobExecutor implements WhateverService {
private final Job myJob;
public JobExecutor(Map<String, Job> allJobImpls,
@Value("${myClass.type}") final String classType) {
this.myJob = allImpls.get(classType);
}
public X myAction() {
// some code
return myJob.whatever(); //for example
}
}