如何使用运行时“限定符"动态地注入服务?多变的? [英] How to dynamically inject a service using a runtime "qualifier" variable?

查看:62
本文介绍了如何使用运行时“限定符"动态地注入服务?多变的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I can't find a simple way to inject a component/service given a runtime value.

我开始阅读@ Spring的文档:

I started reading @ Spring's doc: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-autowired-annotation-qualifiers but I can't find there how to variabilize the values passed to the @Qualifier annotation.

假设我有一个具有此类接口的模型实体:

Let's say I've got a model entity with such interface:

public interface Case {

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

}

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

In my client code, I would do something like:

@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(请参见下面的相关代码).

I expect the caseService to be the UKCaseService (see related code below).

public interface CaseService {

    void modify(Case caze);

}

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

}

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

}

因此,我该如何通过使用一个或所有Spring功能以最简单/优雅/有效的方式修复"所有这些,因此基本上没有.properties,NO XML,仅是注释. 但是我已经怀疑我的DoService中有问题,因为Spring在注入caseService之前需要知道"case" ...但是如何在没有客户端代码知道caseService的情况下实现这一点呢? 我想不通...

So how do I "fix" all of this in the most simple / elegant / efficient way by using either/all Spring feature(s), so essentially NO .properties, NO XML, only annotations. However I already suspect something is wrong in my DoService because Spring would need to know the "case" before injecting the caseService... but how to achieve this without the client code knowing about the caseService?! I can't figure this out...

我已经在此处阅读了一些问题,但是大多数时候它们要么没有真正满足我的需求和/或配置,要么发布的答案不足以让我满意(看起来像'本质上是变通方法,或者(旧)使用(旧)Spring功能).

I already read several issues here on SO, but most of the times either they don't really have the same needs and/or config as I have, or the posted answers aren't enough satisfying to me (look like they're essentially workarounds or (old) usage of (old) Spring features).

​​如何找到多个匹配的bean时,Spring会按名称自动布线吗? =>仅引用类似组件的类

How does Spring autowire by name when more than one matching bean is found? => only refers to component-like classes

动态定义在Spring中自动装配哪个bean(使用限定词) =>确实很有趣,但最详尽的答案(4票)是...差不多3 1/2岁?! (2013年7月)

Dynamically defining which bean to autowire in Spring (using qualifiers) => really interesting but the most elaborated answer (4 votes) is... almost 3 1/2 years-old?! (July 2013)

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

Spring 3 - Dynamic Autowiring at runtime based on another object attribute => quite similar problem here, but the answer really look like a workaround rather a real design pattern (like factory)? and I don't like implementing all the code into the ServiceImpl as it's done...

Spring @Autowiring,如何使用对象工厂来选择实现? =>第二个答案似乎很有趣,但是它的作者没有扩展,所以我对Java Config&完全了解了一点.东西,我不太确定他在说什么...

Spring @Autowiring, how to use an object factory to choose implementation? => 2nd answer seems interestingly but its author does not expand, so altough I know (a bit) about Java Config & stuff, I'm not really sure what he's talking about...

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

How to inject different services at runtime based on a property with Spring without XML => interesting discussion, esp. the answer, but the user has properties set, which I don't have.

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

Also read this: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html#expressions-bean-references => I can't find expanded examples about the use of "@" in expressions. Does someone know about this?

发现了其他类似的问题,没有人得到正确的答案: 如何使用@Autowired进行动态注入像工厂模式一样实施 Spring Qualifier和属性占位符 Spring:将@Qualifier与属性占位符一起使用 如何在Spring中进行条件自动布线? 春季的动态注入 @Qualifier中的SpEL指的是同一bean 如何使用SpEL注入结果Spring中的方法调用?

Found other related-to-similar issues, no one got a proper answer: How to use @Autowired to dynamically inject implementation like a factory pattern Spring Qualifier and property placeholder Spring: Using @Qualifier with Property Placeholder How to do conditional auto-wiring in Spring? Dynamic injection in Spring SpEL in @Qualifier refer to same bean How to use SpEL to inject result of method call in Spring?

工厂模式可能是一种解决方案? 如何使用@Autowired进行动态注入像工厂模式一样实现

Factory Pattern might be a solution? How to use @Autowired to dynamically inject implementation like a factory pattern

推荐答案

您可以使用 附带说明.使用国家/地区代码之类的名称作为bean名称看起来有些奇怪.至少添加一些前缀或更好地考虑其他设计模式.

A side note. Using something like country code as bean name looks a bit odd. Add at least some prefix or better consider some other design pattern.

如果您仍然希望每个国家都有豆子,我建议您使用另一种方法.引入注册服务以按国家/地区代码获取所需的服务:

If you still like to have bean per country, I would suggest another approach. Introduce a registry service to get a required service by country code:

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

示例用法:

@Service
public class DoService {

  @Autowired CaseServices caseServices;

  public void doSomethingWith(Case case){
    CaseService service = caseServices.getCaseService(case.getCountryCode());
    service.modify(case);
  }
}

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

In this case you have to add String getCountryCode() method to your CaseService interface.

public interface CaseService {
    void modify(Case case);
    String getCountryCode();
}

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

Alternatively, you can add method CaseService.supports(Case case) to select the service. Or, if you cannot extend the interface, you can call CaseServices.register(String, CaseService) method from some initialiser or a @Configuration class.

更新:忘记了,Spring已经提供了一个不错的插件抽象可重用样板代码来创建PluginRegistry这样的

UPDATE: Forgot to mention, that Spring already provides a nice Plugin abstraction to reuse boilerplate code for creating PluginRegistry like this.

示例:

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

这篇关于如何使用运行时“限定符"动态地注入服务?多变的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆