CDI Features(EL(SPEL),Decorator,Interceptor,Producer)

1、EL(SPEL) php

ELcss

1.概述;
EL是JSP內置的表達式語言,用以訪問頁面的上下文以及不一樣做用域中的對象 ,
取得對象屬性的值,或執行簡單的運算或判斷操做。EL在獲得某個數據時,會自動進行數據類型的轉換。
使用EL表達式輸出數據時,若是有則輸出數據,若是爲null則什麼也不輸出。
2.語法:
a.EL表達式老是放在{}中,並且前邊有一個$做爲前綴:${}
b.獲取對象的屬性值能夠直接經過「對象.屬性名」:${user.name};
注意:這裏的屬性名是get和set方法對應的屬性值,並非對象中的變量名。
c.獲取對象的屬性也能夠經過「對象[「屬性名」]」:${user["name"]}
d.獲取Map中屬性時能夠以直接經過屬性的key:${map.key},${map[key]}
e.在指定域中獲取屬性:
在EL表達式中若是咱們直接使用屬性名如:${user},它將會在四個域中由小到大依次查找。
順序:pageScope、requestScope、sessionScope、applicationScope。
也能夠指定從哪一個域中獲取:
${ pageScope .user }:當前頁面
${requestScope.user}:當前請求
${sessionScope.user}:當前會話
${sessionScope.user}:當前應用
3.EL中包含11個隱含對象,這些對象能夠在EL表達式中直接使用:
a.pageContext,和JSP中的pageContext功能同樣java

b.請求域:pageScope/requestScope/sessionScope/applicationScope
c.請求參數,參數對象主要用於獲取get或post請求中的參數:
param:獲取指定的請求參數,${param.username}
paramValues:獲取請求參數數組,如:${paramValues.sport[1]}
d.其餘:header/headerValues/initParam/cookie正則表達式

4.EL支持數學運算和邏輯運算:
a.加減乘除:${17+5} => 22
b.取餘%或mod:${17%5} => 2
c.比較運算符>,<,==,!=,<=,>=,eq,ne,lt,gt,le,ge:${3>5}或${3 gt 5} =>false
d.邏輯比較 &&或and,!或not,||或or,empty:${!true} => false

spring

SPEL
1.Spring框架的表達式語言(簡稱SpEL):是一個支持運行時查詢和操做對象圖的強大的表達式語言。
SpEL 爲 bean 的屬性進行動態賦值提供了便利.
2.語法:SpEL 使用 #{…} 做爲定界符,全部在大框號中的字符都將被認爲是 SpEL。
3.運用範圍:
a. 對 bean 進行引用,調用屬性值:#{book.name}
b.調用方法以及引用對象中的屬性
引用方法:#{dog.run()},引用靜態方法:#{T(java.lang.Math).PI}
引用對象的屬性:#{user.name}
c.計算表達式的值
加減乘除:#{counter.total + 40},#{T(java.lang.Math).PI * 2}
加號做爲字符串鏈接符:#{user.name + ' ' + user.address}
比較運算符(>,<,=,>=,<=,==,lt,gt,eq,le,ge):
#{counter.total == 100},#{counter.total le 1000}
if-else條件判斷,三元運算符:
#{user.name=='Tom' ? 'Jess'}shell

d.正則表達式的匹配(matches)
#{user.name matches '^[a-zA-Z0-9_-]{4,16}$'}
e.字面量的表示:
#{5},#{89.7},#{1e4},#{false}
可以使用單/雙引號做爲字符串表達符號:#{'Chuck'},#{"Chuck"}

設計模式

2、Decorator:數組

1.Decorator 設計模式的特色:安全

Decorator 設計模式正如毛胚房的裝修,不會改變原毛胚房的基本框架,只是增長新的外觀、功能等,且隨着時間的推移,能夠不斷的實施裝修工程:增長新的傢俱、根據心情換換新鮮的牆紙等等。在面向對象的程序設計中,擴展系統的原有功能也能夠採用繼承、組合的方式。繼承也不會改變毛胚房(父類),可是因爲裝修工程的複雜和不少不可預測的改變,好比不一樣牆紙和地板樣式的組合數量簡直沒法想一想,難道咱們要爲每一種組合都定義一個子類嗎?顯然這是不現實的,即經過繼承的方式來應對將來的功能和外觀改變一般是吃力不討好的事情。組合的方式也不可取,由於這要求不斷的修改父類的結構,至關於對毛胚房大動干戈,房屋的可維護性和可靠性就大大下降了。服務器

讓咱們回顧一下設計模式的重要原則:Classes should be open for extenstion, but closed for modification。Decorator 設計模式很好的詮釋了這個原則。

2.CDI 對 Decorator 設計模式的支持:

Decorator 設計模式雖然下降了需求變動對軟件開發的影響,可是經過層層包裝,即層層 new 操做建立對象的方式不夠優雅。CDI 容器能夠管理組件的生命週期,在大部分狀況下咱們無須經過 new 操做建立所須要的對象。CDI 中的 Decorator/Delegate 註解很大程度上簡化了 Decorator 設計模式的代碼編寫量,好比實現上面相同的功能,藉助於 CDI,就無須 RoomDecorator 這個抽象類了,全部的 Decorator 類直接實現 Room 接口並使用註解聲明爲 Decorator 便可

3.總結:

Decorator 設計模式簡單而精巧,它實際上是 Unix 哲學的體現:每個應用程序都盡力作好本身,而後經過應用程序之間的協做完成更復雜的任務,正如 shell 的管道符的做用。從複雜應用程序框架設計的角度看,Decorator 設計模式也下降了模塊之間的耦合度,而 CDI 更進了一步,藉助於容器和類型安全的組件模型,簡化了 Decorator 模式的應用,同時消除了某些潛在的運行時異常,也就是說,CDI 之上的 Decorator 設計模式可以幫助構建更加安全的複雜應用。

3、Interceptor

1.攔截器綜述:

攔截器的功能是定義在Java攔截器規範。

攔截器規範定義了三種攔截點: 

  1. 業務方法攔截,
  2. 生命週期回調偵聽,
  3. 超時攔截(EJB)方法。

在容器的生命週期中進行攔截 

public class DependencyInjectionInterceptor { @PostConstruct public void injectDependencies(InvocationContext ctx) { ... } }

EJB超時時使用的攔截器 

public class TimeoutInterceptor { @AroundTimeout public Object manageTransaction(InvocationContext ctx) throws Exception { ... } }

在業務上,對某一個Bean的方法進行攔截 

public class TransactionInterceptor { @AroundInvoke public Object manageTransaction(InvocationContext ctx) throws Exception { ... }

2.攔截器綁定(Interceptor bindings)

假設咱們想要申明一些bean的事務。咱們先要的是一個攔截器綁定類型來指定哪些bean咱們要申明. 
首先定義一個註解 
@InterceptorBinding @Target({METHOD, TYPE}) @Retention(RUNTIME) public @interface Transactional {}

2.攔截器實現(Implementing interceptors)

咱們實際上要實現提供了這種事務管理方面的攔截器,因此咱們須要作的是建立一個標準的攔截,並配上@Interceptor和@transactional註解. 
@Transactional @Interceptor public class TransactionInterceptor { @AroundInvoke public Object manageTransaction(InvocationContext ctx) throws Exception { ... } }
攔截器能夠利用依賴注入: 
@Transactional @Interceptor public class TransactionInterceptor { @Resource UserTransaction transaction; @AroundInvoke public Object manageTransaction(InvocationContext ctx) throws Exception { ... } }

多個攔截器可使用相同的攔截器綁定類型。

3.啓用攔截器(Enabling interceptors)

默認狀況下,全部攔截器被禁用.要使用攔截器.須要在bean.xml中進行配置,以啓用.從CDI 1.1起攔截器可使用@Priority註釋爲整個應用程序啓用。 

<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"> <interceptors> <class>org.mycompany.myapp.TransactionInterceptor</class> </interceptors> </beans>

這樣有2個好處:

  • 攔截器比較重要,在XML中確保其肯定性行爲
  • 它讓咱們在部署時啓用或禁用攔截器類。

固然也能夠配置啓用多個攔截器

<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"> <interceptors> <class>org.mycompany.myapp.SecurityInterceptor</class> <class>org.mycompany.myapp.TransactionInterceptor</class> </interceptors> </beans>

攔截器畢竟比較重要,不推薦使用@Priority啓用.
在CDI中,XML配置的優先級高於@Priority.

4.Interceptor bindings with members(攔截器註解屬性)

假設咱們想要添加一些額外的信息給咱們的@transactional註解: 

@InterceptorBinding @Target({METHOD, TYPE}) @Retention(RUNTIME) public @interface Transactional { boolean requiresNew() default false; }
CDI將使用requiresNew的值選擇兩個不一樣的攔截器,TransactionInterceptor和RequiresNewTransactionInterceptor 


下面是requiresNew爲true的攔截器

@Transactional(requiresNew = true) @Interceptor public class RequiresNewTransactionInterceptor { @AroundInvoke public Object manageTransaction(InvocationContext ctx) throws Exception { ... } }

以下使用:

@Transactional(requiresNew = true) public class ShoppingCart { ... }

可是若是咱們只有一個攔截器,咱們但願容器攔截器綁定時忽略requiresNew的值,也許這些信息只用於攔截器實現。咱們可使用@Nonbinding註釋: 

@InterceptorBinding @Target({METHOD, TYPE}) @Retention(RUNTIME) public @interface Secure { @Nonbinding String[] rolesAllowed() default {}; }

5.Multiple interceptor binding annotations(多重攔截器綁定註解)

一般咱們使用攔截器綁定的組合類型綁定多個攔截器bean。例如,下面的聲明將用於綁定TransactionInterceptor和SecurityInterceptor這2個攔截器到ShoppingCart.

@Secure(rolesAllowed="admin") @Transactional public class ShoppingCart { ... }

然而,在很是複雜的狀況下,一個攔截器自己可能指定攔截器綁定類型:

@Transactional @Secure @Interceptor public class TransactionalSecureInterceptor { ... }

那麼這個攔截器能夠綁定到checkout() 方法,如下任何組合均可使用:

public class ShoppingCart { @Transactional @Secure public void checkout() { ... } }
@Secure public class ShoppingCart { @Transactional public void checkout() { ... } }
@Transactional public class ShoppingCart { @Secure public void checkout() { ... } }
@Transactional @Secure public class ShoppingCart { public void checkout() { ... } }

6. Interceptor binding type inheritance(攔截器綁定類型繼承)

Java語言支持註解的一個限制就是缺少註解繼承.註解應該重用內置已有的.就如同下面這段代碼表達的意思

//實際沒這寫法 public @interface Action extends Transactional, Secure { ... }

幸運的是,CDI圍繞Java沒有的這個特性開展了一些工做. 
咱們會標註一個攔截器綁定類型,其有其餘攔截器的綁定類型,(稱爲元註解) 
表述起來有點費勁,就如同下面代碼這樣.

@Transactional @Secure @InterceptorBinding @Target(TYPE) @Retention(RUNTIME) public @interface Action { ... }

如今任何Bean綁定 Action這個註解 ,其實就是綁定到了@Transactional @Secure.(就是攔截器TransactionInterceptor和攔截器SecurityInterceptor). (甚至TransactionalSecureInterceptor,若是它存在.)

7.Use of @Interceptors(同時用多個攔截器)

這個註解@Interceptors是攔截器規範定義的,cdi是支持的<使用託管bean和EJB規範>.以下:

@Interceptors({TransactionInterceptor.class, SecurityInterceptor.class}) public class ShoppingCart { public void checkout() { ... } }

但缺點也很明顯,不推薦使用.缺點以下:

  1. 攔截器在代碼中是硬編碼.
  2. 攔截器在部署時很差更改.
  3. 攔截器命令是非全局的——它是在類級別由攔截器的順序列出.

所以仍是使用上面CDI的使用方式比較好.

4、Producer

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

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 運算符來實例化。

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。

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(); } } }

結束.

相關文章
相關標籤/搜索