在基于Java的spring配置层次结构中重写bean
假设我们有一个可以为某些客户定制的应用程序。应用程序使用基于Java的spring配置(又称Java配置)进行依赖项注入。应用程序由模块及其子模块组成。每个模块和子模块都有自己的在基于Java的spring配置层次结构中重写bean,java,spring,spring-java-config,Java,Spring,Spring Java Config,假设我们有一个可以为某些客户定制的应用程序。应用程序使用基于Java的spring配置(又称Java配置)进行依赖项注入。应用程序由模块及其子模块组成。每个模块和子模块都有自己的@Configuration类,该类由父配置使用@Import导入。这将创建以下层次结构: MainConfig ----------+---------------- .... | | M
@Configuration
类,该类由父配置使用@Import
导入。这将创建以下层次结构:
MainConfig
----------+---------------- ....
| |
ModuleAConfig ModuleBConfig
|--------------------|
| |
SubModuleA1Config SubModuleA2Config
例如,moduleConfig
如下所示:
@Configuration
@Import({SubModuleA1Config.class, SubModuleA2Config.class})
public class ModuleAConfig {
// some module level beans
}
假设SubModuleA1Config
定义了someBean类型的beansomeBean
:
@Configuration
public class SubModuleA1Config {
@Bean
public SomeBean someBean() { return new SomeBean(); }
}
现在我想为Customer1(C1)定制应用程序-我想使用C1SomeBean
(扩展SomeBean
)而不是SomeBean
作为SomeBean
如何以最少的重复实现这一点
我的一个想法是用继承自MainConfig
的C1Config
、继承自moduleconfig
的c1moduleconfig
和继承自SubModuleA1Config
的C1SubModuleA1Config
来准备替代层次结构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(作用域)管理的工厂多,因此您已经有了一个用于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();
}
}
更新
使用导入选择器:
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
}
不过,由于每个客户都有一个单独的包,所以也要考虑使用<代码> @ CypTrimeStudio。这将选择当前的配置类,不需要额外的配置属性
@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();
}
}
我更喜欢将特定于客户的配置放在一个地方,而不是分散在所有不同的配置类中。此外,如果有3个以上的客户,这样的代码看起来不太好。反对的第三个论点是,我交付的每个包都必须为所有客户提供所有代码(除非我使用了使解决方案更加丑陋的反射)。我只想向客户提供core+C1代码1。但是,
@ImportSelector
可能有助于实现这种模块化。您将如何应用它来获得结果?使用它的例子很少,关于导入选择器,我看到了这个想法,但是IMHO在配置类中仍然存在继承问题,非客户bean覆盖客户bean。可能更好的办法是在非客户配置已经加载时,实现delferredimportselector
以在最后执行导入。这样,客户配置将覆盖非客户配置。关于“工厂解决方案”,如果我有一组定义良好的bean可以被覆盖,它就会工作。就我而言,这是真的。因此接受了答案。一个修正:@组件公共类C1CustomerFactory…
@DawidBytel哦!thanx用于修复。请注意,在配置类中扩展和重写bean方法是合法的。问题是,从超类导入的内容也会被解析。