[英]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.