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

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

问题描述

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

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

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

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,没有 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...

我已经在这里阅读了几个关于 SO 的问题,但大多数情况下,要么他们的需求和/或配置与我不同,要么发布的答案对我来说不够满意(看起来像他们'本质上是(旧的)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 是否按名称自动装配?=> 仅指类组件的类

在 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...

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

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 限定符和属性占位符Spring:使用带有属性占位符的@Qualifier如何在 Spring 中进行条件自动装配?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

推荐答案

您可以使用 BeanFactory:

@Service
public class Doer {

  @Autowired BeanFactory beans;

  public void doSomething(Case case){
    CaseService service = beans.getBean(case.getCountryCode(), CaseService.class)
    service.doSomething(case);
  }
}

附注. 使用类似国家/地区代码的内容作为 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天全站免登陆