如何使用运行时"限定符"变量动态注入服务?

max*_*yme 18 service spring dependency-injection autowired

在给定运行时值的情况下,我找不到一种简单的方法来注入组件/服务.

我开始阅读@ Spring的文档:http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-autowired-annotation-qualifiers 但我找不到如何变量传递给@Qualifier注释的值.

假设我有一个具有这种界面的模型实体:

public interface Case {

    String getCountryCode();
    void setCountryCode(String countryCode);

}
Run Code Online (Sandbox Code Playgroud)

在我的客户端代码中,我会做类似的事情:

@Inject
DoService does;

(...)

Case myCase = new CaseImpl(); // ...or whatever
myCase.setCountryCode("uk");

does.whateverWith(myCase);
Run Code Online (Sandbox Code Playgroud)

......我的服务是:

@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);
    }

}
Run Code Online (Sandbox Code Playgroud)

我希望caseService是UKCaseService(参见下面的相关代码).

public interface CaseService {

    void modify(Case caze);

}

@Service
@Qualifier("uk")
public class UKCaseService implements CaseService {

}

@Service
@Qualifier("us")
public class USCaseService implements CaseService {

}
Run Code Online (Sandbox Code Playgroud)

那么如何通过使用/所有Spring特性以最简单/优雅/有效的方式"修复"所有这些,所以基本上没有.properties,NO XML,只有注释.但是我已经怀疑我的DoService出了什么问题,因为在注入caseService之前Spring需要知道"case"...但是如果没有客户端代码知道caseService,如何实现这个呢?我无法弄明白......

我已经在SO上阅读了几个问题,但是大多数时候要么他们没有和我一样的需求和/或配置相同,要么发布的答案对我来说不够令人满意(看起来他们基本上都是(旧)Spring功能的变通方法或(旧)用法.

当找到多个匹配的bean时,Spring如何按名称自动装配? =>仅指类似组件的类

动态定义哪个bean在Spring中自动装配(使用限定符) =>真的很有趣,但最详细的答案(4票)是......差不多3年半了?!(2013年7月)

Spring 3 - 动态自动装配在运行时基于另一个对象属性 =>这里非常相似的问题,但答案真的看起来像一个变通方法而不是真正的设计模式(如工厂)?我不喜欢将所有代码实现到ServiceImpl中,因为它已经完成了...

Spring @Autowiring,如何使用对象工厂来选择实现? =>第二个答案看起来很有趣,但它的作者没有扩展,所以尽管我知道(有点)关于Java Config和东西,我不太确定他在谈论什么......

如何在运行时基于具有Spring的属性注入不同的服务而不使用XML =>有趣的讨论,尤其是 答案,但用户有属性设置,我没有.

另请阅读:http: //docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html#expressions-bean-references =>我找不到关于使用的扩展示例表达式中的"@".有人知道吗?

编辑:找到其他相关的相似问题,没有人得到一个正确的答案: 如何使用@Autowired动态注入实现,如工厂模式 Spring限定符和属性占位符 Spring:将@Qualifier与Property Placeholder一起使用 如何进行条件自动春天接线? @Qualifier 中Spring SpEL中的 动态注入引用相同的bean 如何使用SpEL在Spring中注入方法调用的结果?

工厂模式可能是一个解决方案? 如何使用@Autowired动态注入工厂模式之类的实现

aux*_*aux 17

您可以使用BeanFactory通过名称动态获取上下文中的bean :

@Service
public class Doer {

  @Autowired BeanFactory beans;

  public void doSomething(Case case){
    CaseService service = beans.getBean(case.getCountryCode(), CaseService.class)
    service.doSomething(case);
  }
}
Run Code Online (Sandbox Code Playgroud)

旁注.使用国家代码之类的东西作为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);
  }
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

@Service
public class DoService {

  @Autowired CaseServices caseServices;

  public void doSomethingWith(Case case){
    CaseService service = caseServices.getCaseService(case.getCountryCode());
    service.modify(case);
  }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您必须String getCountryCode()向您的CaseService界面添加方法.

public interface CaseService {
    void modify(Case case);
    String getCountryCode();
}
Run Code Online (Sandbox Code Playgroud)

或者,您可以添加方法CaseService.supports(Case case)来选择服务.或者,如果您无法扩展界面,则可以CaseServices.register(String, CaseService)从某个初始化程序或@Configuration类调用方法.

更新:忘了提一下,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);
  }
}
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

16322 次

最近记录:

6 年 前