Java 动态定义要在Spring中自动关联的bean(使用限定符)
我有一个JavaEE+Spring应用程序,它支持注释而不是XML配置。bean始终具有原型范围 我现在在我的应用程序中有了业务规则,这些规则取决于用户请求来自的国家。所以我会有这样的想法(请记住,这个例子被大大简化了): 我正在寻找一种方法,使自动布线机制根据当前请求的国家自动注入正确的bean(本例中为美国或加拿大)。国家/地区将存储在ThreadLocal变量中,并且它将在每个请求中更改。对于所有没有自己特定规则的国家来说,也会有一个全球等级 我想我必须定制Spring决定如何创建它将注入的对象的方式。我发现实现这一点的唯一方法是使用FactoryBean,但这并不是我所希望的(不够通用)。我希望做这样的事情:Java 动态定义要在Spring中自动关联的bean(使用限定符),java,spring,Java,Spring,我有一个JavaEE+Spring应用程序,它支持注释而不是XML配置。bean始终具有原型范围 我现在在我的应用程序中有了业务规则,这些规则取决于用户请求来自的国家。所以我会有这样的想法(请记住,这个例子被大大简化了): 我正在寻找一种方法,使自动布线机制根据当前请求的国家自动注入正确的bean(本例中为美国或加拿大)。国家/地区将存储在ThreadLocal变量中,并且它将在每个请求中更改。对于所有没有自己特定规则的国家来说,也会有一个全球等级 我想我必须定制Spring决定如何创建它将注入
谢谢。您可以提供一个配置类,该类将根据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++ } );
}
}
@配置
公共类应用程序配置
{
专用静态整数计数器=0;
@Bean(name=“joel”)
@范围(value=“request”,proxyMode=ScopedProxyMode.TARGET\u类)
列表getJoel()
{
返回Arrays.asList(新字符串[]{“Joel”+counter++});
}
}
并引用控制器中的值,如下所示
@Resource( name="joel" )
private List<String> joel;
@Resource(name=“joel”)
私人名单乔尔;
在提供程序的实现中,可以检查ThreadLocal的区域设置,并返回正确的TransactionRules对象或类似的内容。ScopedProxy的东西是因为我注入了一个控制器,它是单例范围的,而值是请求范围的。创建自己的注释,用于修饰实例变量或setter方法,然后是处理注释并注入通用代理的后处理器,该代理在运行时解析正确的实现并将调用委托给它
@Component
public class TransactionService {
@LocalizedResource
private TransactionRules rules;
//..
}
@Retention(RUNTIME)
@Target({FIELD, METHOD})
public @interface LocalizedResource {}
以下是bean后处理器中的postProcessBeforeInitialization(bean,beanName)
方法的算法:
InjectionMetadata
。您可以通过在spring代码中搜索对该类的引用来查找它如何工作的示例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
s,每种语言环境感知接口实现一个限定符。您可以通过以下命名约定猜测实现的区域设置:如果区域设置感知接口称为TransactionRules,则实现可以在同一个包中命名为TransactionRules_ISOCODE
如果您负担不起这样的命名约定,那么您将需要某种类路径扫描+一种猜测给定实现的区域设置的方法(可能是实现类上的注释)。类路径扫描是可能的,但非常复杂和缓慢,所以尽量避免它
以下是所发生情况的摘要:
不是很琐碎,但它是有效的。这实际上是Spring处理@PersistenceContext的方式,除了实现查找,这是用例的一个附加功能。现在是2017年,难道没有更简单的方法来实现这一点吗??
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
}
}