CDI(Weld)高級<1>CDI高級說明以及Producer methods

CDI是爲解耦而生.如Spring主要用途是AOP.IOC(DI),而CDI除了DI外,AOP功能也是有的.從實際使用上來看,CDI比Spring功能更豐富,更靈活,其代價也是有的,學習成本相對spring較高.

1.CDI致力於鬆耦合,強類型.

實現鬆散耦合的三種方式:
  1. 部署時候的多態選擇,@alternatives 
  2. producer methods在運行時的多態.
  3. 上下文相關的生命週期管理與bean生命週期解耦。
這些技術使客戶端和服務器的鬆散耦合服務。客戶端再也不是緊密地綁定到一個接口的一個實現,也不須要管理生命週期實現。這種方法容許有狀態的對象看成服務交互。鬆散耦合使系統更具活力。在之前,框架老是犧牲了類型安全(尤爲是經過使用XML描述符,Spring2.5)。

CDI提供了三個額外的重要措施,進一步鬆耦合:
  1. 在業務邏輯層用攔截器技術解耦.
  2. 修飾符(註解)能夠用來分離一些業務問題
  3. 用CDI EVENT技術進行解耦事件生產者與消費者.
第二個CDI是強類型的.不管是依賴關係的信息,攔截器,修飾符的Bean,以及CDI event的生產者,消費者等的信息所有都是類型安全的.由編譯器進行驗證.
CDI是確確實實沒String標識符,如xml配置什麼的.好比Spring2.5用XML配置,其實都是字符串,以及"約定大於配置"的概念.在CDI裏是沒有的.CDI框架不是隱藏,而是沒有.
這種方法的最明顯好處就是任何IDE均可以提供自動完成,驗證以及最重要的重構!(瞭解JPA的,能夠對比安全類型的查詢和JPQL.若是重構代碼JPQL是很是麻煩的).
還有個好處就是你在識別不一樣的對象,事件,攔截器能夠經過註解而不是字符串名字,這樣你能夠提高代碼質量.

CDI鼓勵開發使用註解.如
@Asynchronous,  
@Secure,
@Updated,
而不是使用複合名稱,
asyncPaymentProcessor,
SecurityInterceptor
DocumentUpdatedEvent.
這也符合代碼大全裏的一些概念.只不過不用費盡心思考慮命名了,這樣更簡潔高效.
註釋是可重用的。他們幫助描述系統的不一樣部分的共同特質。他們幫助咱們分類和理解代碼。他們幫助咱們應對常見問題的經常使用方法。他們使咱們的代碼更簡潔高效.

2.高級功能Producer methods

先貼一段代碼,下面都用到 java

import javax.enterprise.inject.Produces;

@SessionScoped
public class Preferences implements Serializable {

   private PaymentStrategyType paymentStrategy;

   ...

   @Produces @Preferred 
   public PaymentStrategy getPaymentStrategy() {
       switch (paymentStrategy) {
           case CREDIT_CARD: return new CreditCardPaymentStrategy();
           case CHECK: return new CheckPaymentStrategy();
           case PAYPAL: return new PayPalPaymentStrategy();
           default: return null;
       } 
   }
}
//注入一個Producer methods
@Inject @Preferred PaymentStrategy paymentStrategy;

A:Producer methods的Scope

Producer methods的默認範圍是@Dependent.
從上面代碼咱們能夠思考一種場景,那就是一個用戶會話中有多個PaymentStrategy對象的實例.若是想改變,咱們能夠在Producer方法上添加一個@SessionSciped註解.
如今,若是一個用戶調用了這個Producer methods,那麼返回的這個PaymentStrategy對象的實例將綁定到會話的上下文.Producer methods不會再實例化另外一個出來.
@Produces @Preferred @SessionScoped
public PaymentStrategy getPaymentStrategy() {
   ...
}
注意:Producer methods不繼承聲明此Producer methods的Bean的Scope.
其實這裏有2個不一樣的Bean:Producer methods(至關於一個Bean)以及聲明這個生產方法的Bean.

B: Injection into producer methods

在Producer methods一開始的實例有一個潛在的問題
CreditCardPaymentStrategy 的實現使用 Java new 運算符來實例化。 spring

private PaymentStrategyType paymentStrategy;

而producer methods應該理解爲一個獨立的Bean,而paymentStrategy是從Preferences 中用new實例化的.因此咱們應該使用下面這種方式來使用producer methods方法. 安全

@Produces @Preferred @SessionScoped
public PaymentStrategy getPaymentStrategy(CreditCardPaymentStrategy ccps,
                                          CheckPaymentStrategy cps,
                                          PayPalPaymentStrategy ppps) {

   switch (paymentStrategy) {
      case CREDIT_CARD: return ccps;
      case CHEQUE: return cps;
      case PAYPAL: return ppps;
      default: return null;
   } 
}

這裏會有問題,若是CreditCardPaymentStrategy 是一個@RequestScope,那這裏必然是要發生錯誤的.由於注入的CreditCardPaymentStrategy Bean實例是request,在@SessionScoped使用前容器就會進行銷燬.那麼就出錯了. 服務器

這是個問題,因此咱們有3種處理方案. 框架

  1.  producer method to @Dependent or @RequestScoped.<最好的方式>
  2. CreditCardPaymentStrategy 更改Scope,但這可能會影響其餘的地方,不是很好.
  3. 使用@New限定符,但在CDI 1.1 @New限定符被棄用。CDI鼓勵應用程序注入@Dependent範圍bean。

C:Use of @New with producer methods<不推薦>

Consider the following producer method:
@Produces @Preferred @SessionScoped
public PaymentStrategy getPaymentStrategy(@New CreditCardPaymentStrategy ccps,
                                          @New CheckPaymentStrategy cps,
                                          @New PayPalPaymentStrategy ppps) {
   switch (paymentStrategy) {
      case CREDIT_CARD: return ccps;
      case CHEQUE: return cps;
      case PAYPAL: return ppps;
      default: return null;
   } 
}
這將會建立一個新的CreditCardPaymentStrategy依賴實例,傳遞到生產方法,依賴對象不會被摧毀,直到會話結束。

在CDI 1.1 @New限定符被棄用。CDI鼓勵應用程序注入@Dependent範圍bean。 async

D:Disposer methods

一些Procucer methods返回的對象須要顯式的破壞。例如,有人須要關閉這個JDBC鏈接:
@Produces @RequestScoped 
Connection connect(User user) {

   return createConnection(user.getId(), user.getPassword());
}
而在一個相同的類中,disposer method能夠進行匹配.
void close(@Disposes Connection connection) {

   connection.close();
}
說明:在同一個類中,disposer method能夠進行匹配類型爲Connection 的Procucer methods,從而在 Procucer methods週期結束後進行jdbc的連接關閉.

以下面這個:
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Disposes;
import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Resources {

	@PersistenceUnit
	private EntityManagerFactory entityManagerFactory;

	@Produces
	@RequestScoped
	protected EntityManager createEntityManager() {

		return entityManagerFactory.createEntityManager();
	}

	//參數必須對應上面方法的返回值
	protected void closeEntityManager(@Disposes EntityManager entityManager) {

		if ( entityManager.isOpen() )
		{
			entityManager.close();
		}
	}
}

結束. ide

相關文章
相關標籤/搜索