CDI(Contexts and Dependency Injection 上下文依賴注入),是JAVA官方提供的依賴注入實現,可用於Dynamic Web Module中,將依賴注入IOC/DI上升到容器級別, 它提供了Java EE平臺上服務注入的組件管理核心,簡化是CDI的目標,讓一切均可以被註解被注入。CDI是爲解耦而生.如Spring主要用途是AOP.IOC(DI),而CDI除了DI外,AOP功能也是有的.從實際使用上來看,CDI比Spring功能更豐富,更靈活,其代價也是有的,學習成本相對spring較高. java
實現鬆散耦合的三種方式:
spring
這些技術使客戶端和服務器的鬆散耦合服務。客戶端再也不是緊密地綁定到一個接口的一個實現,也不須要管理生命週期實現。這種方法容許有狀態的對象看成服務交互。鬆散耦合使系統更具活力。在之前,框架老是犧牲了類型安全(尤爲是經過使用XML描述符,Spring2.5)。
CDI提供了三個額外的重要措施,進一步鬆耦合:
安全
第二個CDI是強類型的.不管是依賴關係的信息,攔截器,修飾符的Bean,以及CDI event的生產者,消費者等的信息所有都是類型安全的.由編譯器進行驗證.
CDI是確確實實沒String標識符,如xml配置什麼的.好比Spring2.5用XML配置,其實都是字符串,以及"約定大於配置"的概念.在CDI裏是沒有的.CDI框架不是隱藏,而是沒有.
這種方法的最明顯好處就是任何IDE均可以提供自動完成,驗證以及最重要的重構!(瞭解JPA的,能夠對比安全類型的查詢和JPQL.若是重構代碼JPQL是很是麻煩的).
還有個好處就是你在識別不一樣的對象,事件,攔截器能夠經過註解而不是字符串名字,這樣你能夠提高代碼質量.
CDI鼓勵開發使用註解.如
@Asynchronous,
@Secure,
@Updated,
而不是使用複合名稱,
asyncPaymentProcessor,
SecurityInterceptor
DocumentUpdatedEvent.
這也符合代碼大全裏的一些概念.只不過不用費盡心思考慮命名了,這樣更簡潔高效.
註釋是可重用的。他們幫助描述系統的不一樣部分的共同特質。他們幫助咱們分類和理解代碼。他們幫助咱們應對常見問題的經常使用方法。他們使咱們的代碼更簡潔高效.
服務器
先貼一段代碼,下面都用到框架
1 import javax.enterprise.inject.Produces; 2 3 @SessionScoped 4 public class Preferences implements Serializable { 5 6 private PaymentStrategyType paymentStrategy; 7 8 ... 9 10 @Produces @Preferred 11 public PaymentStrategy getPaymentStrategy() { 12 switch (paymentStrategy) { 13 case CREDIT_CARD: return new CreditCardPaymentStrategy(); 14 case CHECK: return new CheckPaymentStrategy(); 15 case PAYPAL: return new PayPalPaymentStrategy(); 16 default: return null; 17 } 18 } 19 } 20 //注入一個Producer methods 21 @Inject @Preferred PaymentStrategy paymentStrategy;
Producer methods的默認範圍是@Dependent.
從上面代碼咱們能夠思考一種場景,那就是一個用戶會話中有多個PaymentStrategy對象的實例.若是想改變,咱們能夠在Producer方法上添加一個@SessionSciped註解.
如今,若是一個用戶調用了這個Producer methods,那麼返回的這個PaymentStrategy對象的實例將綁定到會話的上下文.Producer methods不會再實例化另外一個出來. async
1 @Produces @Preferred @SessionScoped 2 public PaymentStrategy getPaymentStrategy() { 3 ... 4 }
注意:Producer methods不繼承聲明此Producer methods的Bean的Scope.
其實這裏有2個不一樣的Bean:Producer methods(至關於一個Bean)以及聲明這個生產方法的Bean.ide
在Producer methods一開始的實例有一個潛在的問題
CreditCardPaymentStrategy 的實現使用 Java new 運算符來實例化。學習
private PaymentStrategyType paymentStrategy;
而producer methods應該理解爲一個獨立的Bean,而paymentStrategy是從Preferences 中用new實例化的.因此咱們應該使用下面這種方式來使用producer methods方法.spa
1 @Produces @Preferred @SessionScoped 2 public PaymentStrategy getPaymentStrategy(CreditCardPaymentStrategy ccps, 3 CheckPaymentStrategy cps, 4 PayPalPaymentStrategy ppps) { 5 6 switch (paymentStrategy) { 7 case CREDIT_CARD: return ccps; 8 case CHEQUE: return cps; 9 case PAYPAL: return ppps; 10 default: return null; 11 } 12 }
這裏會有問題,若是CreditCardPaymentStrategy 是一個@RequestScope,那這裏必然是要發生錯誤的.由於注入的CreditCardPaymentStrategy Bean實例是request,在@SessionScoped使用前容器就會進行銷燬.那麼就出錯了..net
Consider the following producer method:
1 @Produces @Preferred @SessionScoped 2 public PaymentStrategy getPaymentStrategy(@New CreditCardPaymentStrategy ccps, 3 @New CheckPaymentStrategy cps, 4 @New PayPalPaymentStrategy ppps) { 5 switch (paymentStrategy) { 6 case CREDIT_CARD: return ccps; 7 case CHEQUE: return cps; 8 case PAYPAL: return ppps; 9 default: return null; 10 } 11 }
這將會建立一個新的CreditCardPaymentStrategy依賴實例,傳遞到生產方法,依賴對象不會被摧毀,直到會話結束。
在CDI 1.1 @New限定符被棄用。CDI鼓勵應用程序注入@Dependent範圍bean。
一些Procucer methods返回的對象須要顯式的破壞。例如,有人須要關閉這個JDBC鏈接:
1 @Produces @RequestScoped 2 Connection connect(User user) { 3 4 return createConnection(user.getId(), user.getPassword()); 5 }
而在一個相同的類中,disposer method能夠進行匹配.
1 void close(@Disposes Connection connection) { 2 3 connection.close(); 4 }
說明:在同一個類中,disposer method能夠進行匹配類型爲Connection 的Procucer methods,從而在 Procucer methods週期結束後進行jdbc的連接關閉.
以下面這個:
1 import javax.enterprise.context.RequestScoped; 2 import javax.enterprise.inject.Disposes; 3 import javax.enterprise.inject.Produces; 4 import javax.enterprise.inject.spi.InjectionPoint; 5 import javax.persistence.EntityManager; 6 import javax.persistence.EntityManagerFactory; 7 import javax.persistence.PersistenceUnit; 8 9 import org.slf4j.Logger; 10 import org.slf4j.LoggerFactory; 11 12 public class Resources { 13 14 @PersistenceUnit 15 private EntityManagerFactory entityManagerFactory; 16 17 @Produces 18 @RequestScoped 19 protected EntityManager createEntityManager() { 20 21 return entityManagerFactory.createEntityManager(); 22 } 23 24 //參數必須對應上面方法的返回值 25 protected void closeEntityManager(@Disposes EntityManager entityManager) { 26 27 if ( entityManager.isOpen() ) 28 { 29 entityManager.close(); 30 } 31 } 32 }