創建用於修飾實例變量或setter方法的自己的註釋,然後處理註釋並注入通用代理,該代理在運行時解析正確的實現並將調用委託給它。
@Component
public class TransactionService {
@LocalizedResource
private TransactionRules rules;
//..
}
@Retention(RUNTIME)
@Target({FIELD, METHOD})
public @interface LocalizedResource {}
這裏是在bean後處理器的postProcessBeforeInitialization(bean, beanName)
方法的算法:
- 反思,以便找到實例變量或都標註有@LocalizedResource setter方法的bean類。將結果存儲在由類名稱索引的緩存中(只是一張地圖)。你可以使用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
,以便向註冊表中添加新的BeanDefinition
,並具有適當的限定符,每個語言環境感知接口的實現都有一個限定符。您可以通過遵循命名約定來猜測實現的語言環境:如果語言環境感知的接口稱爲TransactionRules,則可以在同一個包中將實現命名爲TransactionRules_ISOCODE。
如果你不能承受這樣的命名約定,你將需要進行某種類路徑掃描+一種猜測給定實現的語言環境的方法(可能是對實現類的註釋)。類路徑掃描是可能的,但相當複雜和緩慢,所以儘量避免它。
這裏的內容做一個小結:
- 當應用程序啓動時,TransactionRules的實現將被發現並bean定義將被創建爲他們每個人,與對應於每個執行的區域限定符。這些bean的bean名稱不相關,因爲根據類型和限定符執行查找。
- 執行期間,在線程局部變量中設置當前語言環境
- 查找您需要的bean(例如TransactionService)。後處理器將爲每個@LocalizedResource實例字段或setter方法注入一個代理。
- 當在TransactionService上調用一個最終進入一些TransactionRules方法的方法時,綁定到代理的調用處理程序根據存儲在線程局部變量中的值切換到正確的實現,然後將調用委託給該實現。
不是很微不足道,但它的工作原理。這實際上是Spring如何處理@PersistenceContext,除了實現查找,這是您的用例的一個附加功能。
現在在2017年,是不是有一個更簡單的方法來實現這一目標? – maxxyme