Spring 如何使用运行时动态注入服务;限定词;变量
在给定运行时值的情况下,我找不到注入组件/服务的简单方法 我开始阅读@Spring的文档: 但是我找不到如何对传递给@Qualifier注释的值进行变量化 假设我有一个具有这样接口的模型实体:Spring 如何使用运行时动态注入服务;限定词;变量,spring,service,dependency-injection,autowired,Spring,Service,Dependency Injection,Autowired,在给定运行时值的情况下,我找不到注入组件/服务的简单方法 我开始阅读@Spring的文档: 但是我找不到如何对传递给@Qualifier注释的值进行变量化 假设我有一个具有这样接口的模型实体: public interface Case { String getCountryCode(); void setCountryCode(String countryCode); } 在我的客户机代码中,我将执行以下操作: @Inject DoService does; (...)
public interface Case {
String getCountryCode();
void setCountryCode(String countryCode);
}
在我的客户机代码中,我将执行以下操作:
@Inject
DoService does;
(...)
Case myCase = new CaseImpl(); // ...or whatever
myCase.setCountryCode("uk");
does.whateverWith(myCase);
。。。我的服务是:
@Service
public class DoService {
@Inject
// FIXME what kind of #$@& symbol can I use here?
// Seems like SpEL is sadly invalid here :(
@Qualifier("${caze.countryCode}")
private CaseService caseService;
public void whateverWith(Case caze) {
caseService.modify(caze);
}
}
我希望caseService是UKCaseService(参见下面的相关代码)
那么,我如何通过使用/all-Spring特性(本质上没有.properties,没有XML,只有注释)以最简单/优雅/高效的方式“修复”所有这些呢。
然而,我已经怀疑我的DoService有问题,因为Spring在注入caseService之前需要知道“案例”。。。但是如何在客户代码不知道caseService的情况下实现这一点?!
我搞不懂这个
我已经在这里读了好几期了,但大多数时候,要么它们的需求和/或配置与我的不一样,要么发布的答案对我来说不够满意(看起来它们基本上是变通方法,或者(旧的)Spring特性的(旧的)用法)
=>仅指类似组件的类
=>非常有趣,但最详细的答案(4票)是。。。快三岁半了?!(2013年7月)
=>这里的问题非常相似,但答案看起来确实像是一个解决方案,而不是一个真正的设计模式(如工厂)?我不喜欢在ServiceImpl中实现所有代码,因为它已经完成了
=>第二个答案似乎很有趣,但它的作者并没有展开,所以尽管我(稍微)了解Java配置和东西,但我不确定他在说什么
=>有趣的讨论,特别是答案,但用户设置了属性,而我没有
还请阅读以下内容:
=>我找不到有关表达式中使用“@”的扩展示例。有人知道这件事吗
编辑:
发现其他与类似问题相关的问题,没有人得到正确答案:
工厂模式可能是一个解决方案?
您可以使用以下方法从上下文中按名称动态获取bean: 旁注。使用类似国家代码的名称作为bean名称看起来有点奇怪。至少添加一些前缀或更好地考虑其他设计模式。 如果你仍然希望每个国家都有豆子,我建议另一种方法。引入注册表服务以按国家/地区代码获取所需服务:
@Service
public class CaseServices {
private final Map<String, CaseService> servicesByCountryCode = new HashMap<>();
@Autowired
public CaseServices(List<CaseService> services){
for (CaseService service: services){
register(service.getCountryCode(), service);
}
}
public void register(String countryCode, CaseService service) {
this.servicesByCountryCode.put(countryCode, service);
}
public CaseService getCaseService(String countryCode){
return this.servicesByCountryCode.get(countryCode);
}
}
在这种情况下,您必须将String getCountryCode()
方法添加到CaseService
接口
public interface CaseService {
void modify(Case case);
String getCountryCode();
}
或者,您可以添加方法CaseService.supports(Case)
来选择服务。或者,如果无法扩展接口,可以从某个初始化器或@Configuration
类调用CaseServices.register(String,CaseService)
方法
更新:忘了提到,Spring已经提供了一个很好的抽象,可以重用样板代码来创建PluginRegistry
例如:
public interface CaseService extends Plugin<String>{
void doSomething(Case case);
}
@Service
@Priority(0)
public class SwissCaseService implements CaseService {
void doSomething(Case case){
// Do something with the Swiss case
}
boolean supports(String countryCode){
return countryCode.equals("CH");
}
}
@Service
@Priority(Ordered.LOWEST_PRECEDENCE)
public class DefaultCaseService implements CaseService {
void doSomething(Case case){
// Do something with the case by-default
}
boolean supports(String countryCode){
return true;
}
}
@Service
public class CaseServices {
private final PluginRegistry<CaseService<?>, String> registry;
@Autowired
public Cases(List<CaseService> services){
this.registry = OrderAwarePluginRegistry.create(services);
}
public CaseService getCaseService(String countryCode){
return registry.getPluginFor(countryCode);
}
}
公共接口案例服务扩展插件{
无效剂量测定(例);
}
@服务
@优先级(0)
公共类SwissCaseService实现CaseService{
无效剂量测定(案例){
//对瑞士的案子做点什么
}
布尔支持(字符串国家代码){
返回countryCode.equals(“CH”);
}
}
@服务
@优先级(有序。最低优先级)
公共类DefaultCaseService实现CaseService{
无效剂量测定(案例){
//默认情况下对该案例进行处理
}
布尔支持(字符串国家代码){
返回true;
}
}
@服务
公营个案服务{
private final PluginRegistry根据这个答案,使用@Qualifier
对您没有多大帮助:
至于另一种战略:
- 如果您是spring boot,则可以使用或另一个
条件
- 查找服务,如
@aux
所示
- 只需一致地命名bean,并在运行时按名称查找它们
请注意,您的用例似乎也围绕在应用程序启动时创建bean的场景展开,但所选择的bean需要在applicationContext完成注入bean后进行解析。事实上,它不是“作为bean名称的国家代码”,它是作为限定值的国家代码。;)但最后,当我在上面提到工厂模式作为结论时,我开始开发类似的东西。我很高兴你找到了解决方案。我仍然认为注册表模式可能更适合这种情况,尽管我理解你的实际情况可能会有所不同,而不是那么简单如您所述。祝您好运+我认为第一个示例有点误导,因为@autowiredbeanfactorybean;
如果没有进一步的代码,它将返回null
我发现注册表解决方案非常优雅,我唯一的问题是,服务公共案例服务(列表服务)如何
自动连线?Spring framework会扫描所有实现CaseInterface的可用bean并将其注入CaseServices吗?不必回答,我已经测试过了,它工作得很好。干得好,伙计!!!我想你必须小心你的工厂模式。你打算使用myCase.setCountryCode(“英国”);
不是线程安全的,除非您使用ThreadLocal
存储CaseService
实例。您可能可以使用来在setter中设置ThreadLocal。调用setCountryCode()时,我不会设置CaseService
)
。这是对象的初始化。为了清晰起见,我进行了编辑。因此,您尝试使用@Qualifier在xml中执行#{bean.myMethod()}的等效操作,对吗?不,真的,因为您示例中的bean
是一个Spring管理的bean(如
@Service
public class DoService {
@Autowired CaseServices caseServices;
public void doSomethingWith(Case case){
CaseService service = caseServices.getCaseService(case.getCountryCode());
service.modify(case);
}
}
public interface CaseService {
void modify(Case case);
String getCountryCode();
}
public interface CaseService extends Plugin<String>{
void doSomething(Case case);
}
@Service
@Priority(0)
public class SwissCaseService implements CaseService {
void doSomething(Case case){
// Do something with the Swiss case
}
boolean supports(String countryCode){
return countryCode.equals("CH");
}
}
@Service
@Priority(Ordered.LOWEST_PRECEDENCE)
public class DefaultCaseService implements CaseService {
void doSomething(Case case){
// Do something with the case by-default
}
boolean supports(String countryCode){
return true;
}
}
@Service
public class CaseServices {
private final PluginRegistry<CaseService<?>, String> registry;
@Autowired
public Cases(List<CaseService> services){
this.registry = OrderAwarePluginRegistry.create(services);
}
public CaseService getCaseService(String countryCode){
return registry.getPluginFor(countryCode);
}
}