创建您自己的注释,用于装饰实例变量或设置方法,然后是处理注释并注入通用代理的后处理器,该代理在运行时解析正确的实现并将调用委托给它。
@Component
public class TransactionService {
@LocalizedResource
private TransactionRules rules;
//..
}
@Retention(RUNTIME)
@Target({FIELD, METHOD})
public @interface LocalizedResource {}
这是 bean 后处理器中 postProcessBeforeInitialization(bean, beanName) 方法的算法:
- 自省 bean 类以查找使用 @LocalizedResource 注释的实例变量或设置方法。将结果存储在由类名索引的缓存(只是一个映射)中。为此,您可以使用 Spring 的
InjectionMetadata。您可以通过在 Spring 代码中搜索对此类的引用来查找有关其工作原理的示例。
- 如果 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 方法时,绑定到代理的调用处理程序会根据存储在线程局部变量中的值切换到正确的实现,然后将调用委托给该实现。
不是很简单,但它确实有效。这实际上是 Spring 处理 @PersistenceContext 的方式,除了实现查找,这是您用例的附加功能。