假设我们有一个可以为某些客户定制的应用程序。该应用程序使用基于 Java 的 spring 配置(又名 Java 配置(进行依赖注入。该应用程序由模块及其子模块组成。每个模块和子模块都有自己的@Configuration
类,该类由父配置使用 @Import
导入。这将创建以下层次结构:
MainConfig
----------+---------------- ....
| |
ModuleAConfig ModuleBConfig
|--------------------|
| |
SubModuleA1Config SubModuleA2Config
例如ModuleAConfig
如下所示:
@Configuration
@Import({SubModuleA1Config.class, SubModuleA2Config.class})
public class ModuleAConfig {
// some module level beans
}
假设SubModuleA1Config
定义了 SomeBean 类型的 bean someBean
:
@Configuration
public class SubModuleA1Config {
@Bean
public SomeBean someBean() { return new SomeBean(); }
}
现在我想为客户1(C1(自定义应用程序 - 我想使用C1SomeBean
(扩展SomeBean
(而不是SomeBean
作为someBean
。
如何以最少的重复实现此目的?
我的想法之一是准备替代层次结构,C1Config
继承自MainConfig
,C1ModuleAConfig
继承ModuleAConfig
,C1SubModuleA1Config
继承SubModuleA1Config
。 C1SubModuleA1Config
将覆盖返回C1SomeBean
someBean()
方法。不幸的是,在Spring 4.0.6中,我得到了类似的东西:
Overriding bean definition for bean 'someBean': replacing [someBean defined in class C1SubmoduleA1Config] with [someBean defined in class SubModuleA1Config]
事实上SomeBean
类是从上下文而不是C1SomeBean
返回的。这显然不是我想要的。
请注意,不能覆盖@Import
扩展配置类。
如果要选择要在运行时使用的导入,可以改用@ImportSelector
。
但是,@Configuration
类并不是 spring(作用域(管理的工厂,因此您已经为某些 Bean 提供了工厂方法,因此您无需进一步操作:
@Configuration
public class SubModuleA1Config {
@Autowired
private Environment env;
@Bean
public SomeBean someBean() {
String customerProperty = env.getProperty("customer");
if ("C1".equals(customerProperty))
return new C1SomeBean();
return new SomeBean();
}
}
更新
使用 ImportSelector:
class CustomerImportSelector implements ImportSelector, EnvironmentAware {
private static final String PACKAGE = "org.example.config";
private static final String CONFIG_CLASS = "SubModuleConfig";
private Environment env;
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
String customer = env.getProperty("customer");
return new String[] { PACKAGE + "." + customer + "." + CONFIG_CLASS };
}
@Override
public void setEnvironment(Environment environment) {
this.env = environment;
}
}
@Configuration
@Import(CustomerImportSelector.class)
public class ModuleAConfig {
// some module level beans
}
但是,由于每个客户都有一个单独的软件包,因此请考虑也使用 @ComponentScan
.这将选取存在的配置类,并且不需要额外的配置属性。
@Configuration
@ComponentScan(basePackages="org.example.customer")
public class SubModuleA1Config {
@Autowired
private CustomerFactory customerFactory;
@Bean
public SomeBean someBean() {
return customerFactory.someBean();
}
}
public interface CustomerFactory {
SomeBean someBean();
}
@Component
public class C1CustomerFactory implements CustomerFactory {
@Override
public SomeBean someBean() {
return new C1SomeBean();
}
}