CDI(Weld)高級<2> Interceptors(攔截器)

1.攔截器綜述

攔截器的功能是定義在Java攔截器規範。 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 { ... }
}
@AroundInvoke註釋指定了要用做攔截器的方法,攔截器方法與被攔截的業務方法執行同一個java調用堆棧、同一個事務和安全上下文中。用@AroundInvoke註釋指定的方法必須遵照如下格式:public Object XXX(javax.interceptor.InvocationContext ctx) throws Exception

下面是javax.interceptor.InvocationContext封裝了客戶端所調用業務方法的一些信息。
package javax.interceptor;
public interface InvocationContext{
    public Object getTarget();
    public Method getMethod();
    public Ojbect[] getParameters();
    public void setParameters(Object[] newArgs);
    public java.util.Map<String, Ojbect> getContextData();
    public Object proceed() throws Exception;
}
  • getTarget() 指向被調用的bean實例
  • getMethod() 指向被攔截的業務方法
  • getParameters() 獲取被攔截業務方法的參數
  • setParameters() 設置被攔截業務方法的參數
  • getContextData() 返回一個Map對象,它在整個方法調用期間均可以被訪問到。位於同一個方法調用內的不一樣攔截器之間能夠利用它來傳遞上下文相關的數據。
示例:
//被攔截的方法
@Interceptors(HelloInterceptor.class) 
public class HelloChinaBean {
    public String SayHello(String name) {
        return name +"Hello World.";
    }
}
//攔截器定義
public class HelloInterceptor {
    @AroundInvoke
    public Object log(InvocationContext ctx) throws Exception {
       
        try{
            if (ctx.getMethod().getName().equals("SayHello")){
                System.out.println("Holle World!!!" );
            }            
            return ctx.proceed();
        }catch (Exception e) {
            throw e;
        } 
    }
}

2.攔截器綁定(Interceptor bindings)

假設咱們想要申明一些bean的事務。咱們先要的是一個攔截器綁定類型來指定哪些bean咱們要申明.
首先定義一個註解
@InterceptorBinding
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Transactional {}
如今咱們能夠很容易地指定類ShoppingCart是事務性對象:
@Transactional
public class ShoppingCart { ... }
或者咱們能夠指定一個方法的事務
public class ShoppingCart {
   @Transactional public void checkout() { ... }
}

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 { ... }
}

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

@Resource和@Inject的區別: app

Injection Mechanism
Can Inject JNDI Resources Directly
Can Inject Regular Classes Directly
Resolves By
Typesafe
Resource Injection
Yes
No
Resource name
No
Dependency Injection
No
Yes
Type
Yes

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個好處: ui

  • 攔截器比較重要,在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.
關於@Priority能夠參考下列: this

public static class Interceptor.Priority
extends Object
Priorities that define the order in which interceptors are invoked. These values should be used with the Priority annotation.

Interceptors defined by platform specifications should have priority values in the range PLATFORM_BEFORE up until LIBRARY_BEFORE, or starting at PLATFORM_AFTER.
Interceptors defined by extension libraries should have priority values in the range LIBRARY_BEFORE up until APPLICATION, or LIBRARY_AFTER up until PLATFORM_AFTER.
Interceptors defined by applications should have priority values in the range APPLICATION up until LIBRARY_AFTER.
An interceptor that must be invoked before or after another defined interceptor can choose any appropriate value.

Interceptors with smaller priority values are called first. If more than one interceptor has the same priority, the relative order of these interceptor is undefined.

For example, an extension library might define an interceptor like this: 編碼

@Priority(Interceptor.Priority.LIBRARY_BEFORE+10)
 @Interceptor
 public class ValidationInterceptor { ... }

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. spa

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

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

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

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

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語言支持註解的一個限制就是缺少註解繼承.註解應該重用內置已有的.就如同下面這段代碼表達的意思 xml

//實際沒這寫法
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的使用方式比較好.
相關文章
相關標籤/搜索