在基于 Java 的 spring 配置层次结构中覆盖 bean
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25252374/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Overriding beans in Java-based spring configuration hierarchy
提问by Dawid Pytel
Let's assume we have an application that can be customized for some customers. The application is using Java-based spring configuration (a.k.a. Java config) for dependency injection. The application consists of modules and their submodules. Each module and submodule has its own @Configuration
class which is imported by parent configuration using @Import
. This creates the following hierarchy:
假设我们有一个可以为某些客户定制的应用程序。该应用程序使用基于 Java 的 spring 配置(又名 Java 配置)进行依赖注入。该应用程序由模块及其子模块组成。每个模块和子模块都有自己的@Configuration
类,由父配置使用@Import
. 这将创建以下层次结构:
MainConfig
----------+---------------- ....
| |
ModuleAConfig ModuleBConfig
|--------------------|
| |
SubModuleA1Config SubModuleA2Config
For example ModuleAConfig
looks like this:
例如ModuleAConfig
看起来像这样:
@Configuration
@Import({SubModuleA1Config.class, SubModuleA2Config.class})
public class ModuleAConfig {
// some module level beans
}
Let's say that SubModuleA1Config
defines bean someBean
of type SomeBean:
假设SubModuleA1Config
定义someBean
了 SomeBean 类型的 bean:
@Configuration
public class SubModuleA1Config {
@Bean
public SomeBean someBean() { return new SomeBean(); }
}
Now I want to customize the application for Customer1 (C1) - I want to use C1SomeBean
(extending SomeBean
) instead of SomeBean
as someBean
.
现在我想为 Customer1 (C1) 自定义应用程序 - 我想使用C1SomeBean
(extending SomeBean
) 而不是SomeBean
as someBean
。
How can I achieve this with minimum duplication?
我怎样才能以最少的重复实现这一目标?
One of my ideas was to prepare alternative hierarchy with C1Config
inheriting from MainConfig
, C1ModuleAConfig
from ModuleAConfig
and C1SubModuleA1Config
from SubModuleA1Config
. C1SubModuleA1Config
would override someBean()
method returning C1SomeBean
. Unfortunately with Spring 4.0.6 I get something like:
我的一个想法是准备替代层次结构,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]
and indeed SomeBean
class is returned from context instead of C1SomeBean
. This is clearly not what I want.
实际上SomeBean
class 是从上下文而不是C1SomeBean
. 这显然不是我想要的。
回答by Jose Luis Martin
Note that you cannot override @Import
extending configuration classes.
请注意,您不能覆盖@Import
扩展配置类。
If you want to select which imports to use at runtime, you could use a @ImportSelector
instead.
如果要选择在运行时使用哪些导入,可以改用 a @ImportSelector
。
However, @Configuration
classes are not more that spring (scoped) managed factories so as you already have a factory method for someBean you don't need to go even further:
但是,@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();
}
}
Update
更新
Using a 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
}
However, as every customer has a a separate package, consider also using @ComponentScan
. This will pick the configuration class present and don't need a extra configuration property.
但是,由于每个客户都有一个单独的包,因此还可以考虑使用@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();
}
}