[英]Dynamically defining which bean to autowire in Spring (using qualifiers)
我有一个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,但这并不是我所希望的(不够通用)。 我希望做这样的事情:
我在正确的道路上吗? 对我有什么想法吗?
谢谢。
创建您自己的注释,该注释用于装饰实例变量或setter方法,然后由后处理器处理该注释,并注入一个通用代理,该代理在运行时解析正确的实现并将其委托给它。
@Component
public class TransactionService {
@LocalizedResource
private TransactionRules rules;
//..
}
@Retention(RUNTIME)
@Target({FIELD, METHOD})
public @interface LocalizedResource {}
这是bean后处理器中的postProcessBeforeInitialization(bean, beanName)
方法的算法:
InjectionMetadata
。 您可以通过在Spring代码中搜索对该类的引用来查找有关其工作方式的示例。 这是用于创建本地化资源的代理的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
,以便使用适当的限定符将新的BeanDefinition
添加到注册表中,每种实现都适用于区域设置感知的接口。 您可以通过遵循以下命名约定来猜测实现的语言环境:如果一个支持语言环境的接口称为TransactionRules,则在同一程序包中的实现可以命名为TransactionRules_ISOCODE。
如果您负担不起这样的命名约定,则将需要某种类路径扫描+一种猜测给定实现的语言环境的方法(可能是实现类的注释)。 类路径扫描是可能的,但是非常复杂且缓慢,因此请避免使用它。
以下是发生的情况的摘要:
并不是很琐碎,但是可以。 实际上,这是Spring处理@PersistenceContext的方式,除了实现查找(这是用例的附加功能)之外。
您可以提供一个Configuration类,该类将基于ThreadLocal值返回正确的bean。 假设您使用的是Spring3。我做了一点测试,以确保在每个请求上都调用了provider方法。 这就是我所做的。
@Configuration
public class ApplicationConfiguration
{
private static int counter = 0;
@Bean( name="joel" )
@Scope( value="request", proxyMode=ScopedProxyMode.TARGET_CLASS)
List<String> getJoel()
{
return Arrays.asList( new String[] { "Joel " + counter++ } );
}
}
并在我的控制器中引用了以下值。
@Resource( name="joel" )
private List<String> joel;
在提供程序的实现中,可以检查ThreadLocal的语言环境,并返回正确的TransactionRules对象或类似的对象。 ScopedProxy的原因是因为我正在注入Controller,该控制器是Singleton范围的,而值是request范围的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.