Java EE CDI 主要使用@Inject註解來實現依賴注入,把受管理的bean注入到由容器管理的其它資源中去。在本教程中,我們將會介紹在CDI環境下幾種不同的可選策略來實現依賴注入。
本教程基於如下環境:
JDK
Weld 1.1.10
Weld 是CDI 的參考實現。
2. 構造器依賴注入
public class SomeBean {
private final Service service;
@Inject
public SomeBean(Service service){
ice = service;
}
}
當CDI容器在初始化一個SomeBean型別的bean例項時,它將會查詢該類的預設構造器(無參構造器)並用它來建立bean例項。但是有一個例外情況,就是當我們還有一個使用@Inject 進行了註解的構造器時,這種情況下,容器會改用有註解的構造器而不是無參構造器,並且把通過構造器引數傳入的依賴資源注入到bean例項中來。
注意: 記住一個類只允許有 一個@Inject註解的構造器。
在上面的例子中,容器將會獲取到一個Service 的例項並把它注入到SomeBean 的註解構造器中。
3. 欄位依賴注入
public class SomeBean {
@Inject
private Service service;
}
這種情況下,當容器初始化一個 SomeBean型別的bean時,它會把一個正確的Service例項注入給該欄位,即使該欄位是一個私有欄位,並且不需要有任何setter方法。
4. 初始化方法依賴注入
public class SomeBean {
private Service service;
@Inject
public void setService(Service service) {
ice = service;
}
}
這種情況下,當容器初始化一個 SomeBean型別的bean時,它會呼叫所有由@Inject註解了的方法,並且通過方法引數的方式把依賴注入進來。
@Any 修飾符
為了提供完全鬆耦合的應用,我們通常把介面注入到受管理的資源中。當我們有多個實現了給定介面的bean時該怎麼辦呢?我們可以同時使用@Any修飾符和CDI的Instance介面,來把所有該介面的實現bean都注入進一個受管理的bean中:
The @Any qualifier
public class SomeBean {
@Inject
public void listServiceImplementations(
@Any Instance
for(Service service : serviceList){
tln(lass()anonicalName());
}
}
}
@Any 修飾符告訴容器,任何可供使用的依賴都適用於該注入點,所以容器會把他們都注入進來。 如果我們有介面的多個實現而我們只注入其中的一個 - 並且沒有做任何排除工作 - 那麼容器將會抱怨並且無法成功的初始化元件。我們將會在其他教程中介紹依賴排除問題。
6.注入到生產者方法中
生產者方法的引數也可以經由CDI容器進行注入。請檢視Java EE CDI Producer methods tutorial.
7. CDI 代理
如果我們不涉及CDI代理機制,那麼本教程將是不完整的。當我們把一個在不同於@Dependent範圍下創建出來的bean注入到另外一個託管資源時,CDI容器不會注入一個被注入bean的直接引用。
CDI 中bean 的範圍請看 Java EE CDI bean scopes
為什麼CDI使用代理? 因為如果bean的直接引用被注入,將會給被管理的bean造成諸如執行緒安全或併發訪問的問題。
設想一下一個Session 範圍的 bean被注入到一個Application範圍的bean中去的情形。由於application 範圍的bean在所有客戶端間共享,如果多個客戶端同時訪問一個application 範圍的`bean,那麼將會存在很高的風險出現這種情況:一個客戶端訪問了其他客戶端正在訪問的session範圍的bean。
為了處理這種問題,CDI創造了代理並把代理注入進注入點。由代理負責處理對被注入bean的呼叫,並實際去呼叫正確的bean例項。
CDI建立的代理繼承自被注入bean的型別。設想一下下面的情形:
Application 和 Session 範圍的 bean
@SessionScoped
public class Service {
public void doWork() {
tln("Working...");
}
}
@ApplicationScoped
public class SomeBean {
@Inject
private Service service;
public void test(){
rk();
}
}
CDI將把一個session範圍的bean的代理注入進一個application範圍的bean中去。每一次對session範圍bean的呼叫,都 將通過代理進行,代理會把呼叫重定向到正確的session範圍bean的例項,那個從屬於正確的HTTP request session的bean。
CDI建立代理是通過繼承原來bean的類,並重寫所有非私有方法。一個簡單的典型的代理的例子可以像下面這樣:
CDI 代理 示例
ublic class Service$Proxy$_$$_WeldClientProxy
extends Service {
@Override
public void doWork() {
Service instance = // ... resolve bean instance
rk();
}
}
由於CDI代理通過繼承bean的類來建立,所以當我們討論非依賴性bean範圍的時候,你應當明白CDI有如下一些限制:
CDI 不能注入原始型別
bean的類必須有一個非私有的預設構造器
bean的類不能是final型別的並且不能有任何final方法