假设我们有一个可以为某些客户定制的应用程序。该应用程序使用基于Java的spring配置(也称为Java配置)进行依赖项注入(inject)。该应用程序由模块及其子模块组成。每个模块和子模块都有自己的@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()
方法,返回C1SomeBean
。不幸的是,在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 (作用域)管理的工厂而已,因此,由于您已经拥有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();
}
}
关于java - 在基于Java的Spring配置层次结构中覆盖Bean,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/25252374/