SeamFramework.orgCommunity Documentation
Web Bean 會重新使用 EJB 3.0 的基本攔截器架構,並朝兩個方向來延伸該功能:
不只是 session bean,任何 Web Bean 都能有攔截器。
針對於將攔截器綁定至 Web Bean,Web Bean 含有較為復雜的標記導向方式。
EJB 格式定義了兩種類型的攔截點:
business method 的攔截,以及
lifecycle callback 的攔截。
business method 攔截器可適用於來自 Web Bean 客戶端的 Web Bean 的 method 調用:
public class TransactionInterceptor {
@AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... }
}
lifecycle callback 攔截器可適用於來自 container 的 lifecycle callback 的調用:
public class DependencyInjectionInterceptor {
@PostConstruct public void injectDependencies(InvocationContext ctx) { ... }
}
攔截器 class 可攔截 lifecycle callback 以及 business method。
假設我們希望宣告我們有些 Web Bean 屬於交易性的 Web Bean。我們首先需要的就是一個 攔截器綁定標記(interceptor binding annotation)來明確指定我們要專注的 Web Bean 是哪個:
@InterceptorBindingType
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Transactional {}
現在我們能輕易地指定我們的 ShoppingCart
屬於一個交易性的物件:
@Transactional
public class ShoppingCart { ... }
或是,若我們想要的話,我們能夠指定只有一個 method 屬於交易性:
public class ShoppingCart {
@Transactional public void checkout() { ... }
}
我們必須在某個階段實際地實做提供此交易管理功能的攔截器。我們只需要建立一個標準的 EJB 攔截器並將它標記為 @Interceptor
和 @Transactional
。
@Transactional @Interceptor
public class TransactionInterceptor {
@AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... }
}
所有的 Web Bean 攔截器皆為基本的 Web Bean,並且能夠有效利用依賴注入(dependency injection)以及 contextual lifecycle 管理。
@ApplicationScoped @Transactional @Interceptor
public class TransactionInterceptor {
@Resource Transaction transaction;
@AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... }
}
多個攔截器可能會使用相同的攔截器綁定類型。
最後,我們需要在 web-beans.xml
中啟用我們的攔截器。
<Interceptors>
<tx:TransactionInterceptor/>
</Interceptors
>
為何會有尖括弧?
XML 宣告可解決兩項問題:
它可讓我們為我們系統中的所有攔截器指定全排序(total ordering),並確保決定型的特性,並且
它讓我們能夠在進行建置時啟用或停用攔截器 class。
比方說,我們能夠指定我們的安全性攔截器能比我們的 TransactionInterceptor
還要早執行。
<Interceptors>
<sx:SecurityInterceptor/>
<tx:TransactionInterceptor/>
</Interceptors
>
或是我們可在測試環境中將它們兩者同時關閉!
假設我們希望新增一些額外的資訊至我們的 @Transactional
標記中:
@InterceptorBindingType
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Transactional {
boolean requiresNew() default false;
}
Web Bean 將會使用 requiresNew
的值來在兩個不同的攔截器(TransactionInterceptor
與 RequiresNewTransactionInterceptor
)之間作選擇。
@Transactional(requiresNew=true) @Interceptor
public class RequiresNewTransactionInterceptor {
@AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... }
}
現在我們可如下使用 RequiresNewTransactionInterceptor
:
@Transactional(requiresNew=true)
public class ShoppingCart { ... }
當綁定攔截器時,若我們只有一個攔截器而我們希望管理員忽略 requiresNew
的值時該怎麼辦呢?這時我們可使用 @NonBinding
標記:
@InterceptorBindingType
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Secure {
@NonBinding String[] rolesAllowed() default {};
}
通常,我們會使用不同攔截器綁定類型的組合來將多重攔截器綁定至 Web Bean。比方說,下列宣告會被用來將 TransactionInterceptor
和 SecurityInterceptor
綁定至相同的 Web Bean:
@Secure(rolesAllowed="admin") @Transactional
public class ShoppingCart { ... }
不過,在非常複雜的情況下,攔截器本身會指定一些攔截器綁定類型的組合:
@Transactional @Secure @Interceptor
public class TransactionalSecureInterceptor { ... }
然後這個攔截器便可透過使用下列任何一個組合來綁定至 checkout()
這個 method:
public class ShoppingCart {
@Transactional @Secure public void checkout() { ... }
}
@Secure
public class ShoppingCart {
@Transactional public void checkout() { ... }
}
@Transactionl
public class ShoppingCart {
@Secure public void checkout() { ... }
}
@Transactional @Secure
public class ShoppingCart {
public void checkout() { ... }
}
Java 語言對於標記上的支援有個限制,那就是缺少了標記的 inheritance。實際上,標記應該要內建 reuse,以便允許這類型的功能有效:
public @interface Action extends Transactional, Secure { ... }
幸運的是,Web Bean 有方法來解決 Java 所缺少的這項功能上的問題。我們可利用其它攔截器綁定類型來標記某個攔截器綁定類型。攔截器綁定為可轉變的 任何含有第一個攔截器綁定的 Web Bean 都會繼承宣告為 meta-annotation 的攔截器綁定。
@Transactional @Secure
@InterceptorBindingType
@Target(TYPE)
@Retention(RUNTIME)
public @interface Action { ... }
任何標記為 @Action
的 Web Bean 都會被綁定至 TransactionInterceptor
和 SecurityInterceptor
。(甚至是 TransactionalSecureInterceptor
,若它存在的話。)
企業級與基本的 Web Bean 皆支援 EJB 規格所定義的 @Interceptors
標記,例如:
@Interceptors({TransactionInterceptor.class, SecurityInterceptor.class})
public class ShoppingCart {
public void checkout() { ... }
}
不過,這個方式有下列缺點:
攔截器的實做會被 hardcode 在 business code 之中、
攔截器無法在進行建置時輕易地被停用,以及
攔截器的順序會是非全域性的 會由攔截器被列於 class 層級的順序來取決。
因此,我們建議使用 Web Bean 形式的攔截器綁定。