假设我们有一个可以为某些客户定制的应用程序。该应用程序使用基于Java的弹簧配置(a.k.a. 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(); }
}
现在我想为Customer1(C1)自定义应用程序 - 我想使用C1SomeBean
(扩展SomeBean
)而不是SomeBean
作为someBean
。
如何以最少的重复实现这一目标?
我的一个想法是准备备用层次结构,其中C1Config
继承自MainConfig
,C1ModuleAConfig
来自ModuleAConfig
,C1SubModuleA1Config
来自SubModuleA1Config
。 C1SubModuleA1Config
将覆盖返回someBean()
的{{1}}方法。不幸的是,在Spring 4.0.6中我得到了类似的东西:
C1SomeBean
实际上 Overriding bean definition for bean 'someBean': replacing [someBean defined in class C1SubmoduleA1Config] with [someBean defined in class SubModuleA1Config]
类是从上下文而不是SomeBean
返回的。这显然不是我想要的。
答案 0 :(得分:9)
请注意,您无法覆盖@Import
扩展配置类。
如果要选择在运行时使用哪些导入,可以使用@ImportSelector
代替。
但是,@Configuration
类不仅仅是spring(作用域)托管工厂,因此您已经拥有someBean的工厂方法,您不需要更进一步:
@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();
}
}