我有一个Java EE + Spring应用程序,它支持XML配置的注释。豆子总是有原型范围。
我现在的应用程序业务规则取决于用户请求所在的国家/地区。所以我会有这样的事情(请记住,这个例子大大简化了):
@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,但这并不是我所希望的(不够通用)。我希望能做到这样的事情:
我是在正确的道路上吗?对我有什么想法吗?
感谢。
答案 0 :(得分:4)
创建自己的注释,用于装饰实例变量或setter方法,然后是一个处理注释并注入通用代理的后处理器,该代理在运行时解析正确的实现并将调用委托给它。
@Component
public class TransactionService {
@LocalizedResource
private TransactionRules rules;
//..
}
@Retention(RUNTIME)
@Target({FIELD, METHOD})
public @interface LocalizedResource {}
以下是bean后处理器中postProcessBeforeInitialization(bean, beanName)
方法的算法:
InjectionMetadata
来实现此目的。您可以通过在spring代码中搜索对此classe的引用来查找有关其工作原理的示例。以下是将用于创建本地化资源的代理的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的方式,除了实现查找,这是用例的附加功能。
答案 1 :(得分:0)
您可以提供一个Configuration类,它将根据ThreadLocal值返回正确的bean。这假设您使用的是Spring 3.我做了一些测试,以确保在每个请求上调用了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作用域,而值是请求作用域。