java – 动态定义在Spring中自动装配哪个bean(使用限定符)

我有一个Java EE Spring应用程序,它支持XML配置的注释. bean总是有原型范围.

我现在的应用程序业务规则取决于用户请求所在的国家/地区.所以我会有这样的事情(请记住,这个例子大大简化了):

@Component
public class TransactionService {
    @Autowired
    private TransactionRules rules;
    //..
}


@Component
@Qualifier("US")
public class TransactionRulesForUS implements TransactionRules {
     //..
}

@Component
@Qualifier("CANADA")
public class TransactionRulesForCanada implements TransactionRules {
     //..
}

我正在寻找一种方法,使自动布线机制根据当前请求的国家/地区自动注入正确的bean(在此示例中为美国或加拿大).该国家/地区将存储在ThreadLocal变量中,并且会在每个请求中更改.对于没有自己特定规则的所有国家,也会有一个全球类.

我想我必须自定义Spring决定如何创建它将注入的对象的方式.我发现这样做的唯一方法是使用FactoryBean,但这并不是我所希望的(不够通用).我希望做这样的事情:

>在Spring实例化一个对象之前,必须调用我自己的自定义代码.
>如果我检测到所请求的接口有多个实现,我会在我的ThreadLocal变量中查找正确的国家/地区,并动态地将相应的限定符添加到自动连线请求中.
>在那之后,Spring会做所有常用的魔法.如果添加了限定符,则必须考虑这一点;如果没有,流程将照常进行.

我在正确的道路上吗?对我有什么想法吗?

谢谢.

最佳答案
创建用于装饰实例变量或setter方法的自己的注释,然后创建一个处理注释并注入通用代理的后处理器,该代理在运行时解析正确的实现并将调用委托给它.

@Component
public class TransactionService {
  @LocalizedResource
  private TransactionRules rules;
  //..
}

@Retention(RUNTIME)
@Target({FIELD, METHOD})
public @interface LocalizedResource {}

以下是bean后处理器中postProcessBeforeInitialization(bean,beanName)方法的算法:

>引入bean类以查找使用@LocalizedResource注释的实例变量或setter方法.将结果存储在由类名索引的缓存(仅地图)中.您可以使用Spring的InjectionMetadata来实现此目的.您可以通过在spring代码中搜索对此classe的引用来查找有关其工作原理的示例.
>如果bean存在这样的字段或方法,请使用下面描述的InvocationHandler创建代理,并将当前BeanFactory传递给它(bean后处理器必须是ApplicationContextAware).在实例变量中注入该代理,或者使用代理实例调用setter方法.

以下是将用于创建本地化资源的代理的InvocationHandler.

public class LocalizedResourceResolver implements InvocationHandler {
  private final BeanFactory bf;
  public LocalizedResourceResolver(BeanFactory bf) {
    this.bf = bf;
  }
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    String locale = lookupCurrentLocale();
    Object target = lookupTarget(locale);
    return method.invoke(target, args);
  }

  private String lookupCurrentLocale() {
    // here comes your stuff to look up the current locale
    // probably set in a thread-local variable
  }

  private Object lookupTarget(String locale) {
    // use the locale to match a qualifier attached to a bean that you lookup using the BeanFactory.
    // That bean is the target
  }
}

您可能需要对bean类型进行更多控制,或者在InvocationHandler中添加所请求的bean类型.

接下来要自动检测给定接口的实现,这些接口是依赖于本地的,并使用与语言环境对应的限定符进行注册.您可以为此目的实现BeanDefinitionRegistryPostProcessor或BeanFactoryPostProcessor,以便将新的BeanDefinitions添加到注册表中,并使用适当的限定符,每个实现的语言环境感知接口都有一个限定符.您可以通过遵循命名约定来猜测实现的区域设置:如果区域设置感知的接口称为TransactionRules,则实现可以在同一个包中命名为TransactionRules_ISOCODE.

如果您无法承担这样的命名约定,则需要使用某种类路径扫描来猜测给定实现的区域设置(可能是实现类的注释).类路径扫描是可能的,但非常复杂和缓慢,所以尽量避免它.

以下是对所发生情况的总结:

>当应用程序启动时,将发现TransactionRules的实现,并为每个实现创建bean定义,其限定符对应于每个实现的语言环境.这些bean的bean名称不相关,因为基于类型和限定符执行查找.
>在执行期间,将当前语言环境设置为线程局部变量
>查找您需要的bean(例如,TransactionService).后处理器将为每个@LocalizedResource实例字段或setter方法注入代理.
>当在TransactionService上调用最终成为某些TransactionRules方法的方法时,绑定到代理的调用处理程序根据存储在thread-local变量中的值切换到正确的实现,然后将调用委托给该实现.

不是很琐碎,但它确实有效.这实际上是Spring处理@PersistenceContext的方式,除了实现查找,这是用例的附加功能.

转载注明原文:java – 动态定义在Spring中自动装配哪个bean(使用限定符) - 代码日志