简单来说, EBJ3有自己的拦截器, Seam框架进行了扩展,普通的JavaBean也可以成为拦截器,只是要间接的使用到JavaBean上。
可以继承org.jboss.seam.intercept.AbstractInterceptor这个类来写。
几个类的import路径: java.lang.annotation.Retention java.lang.annotation.Target java.lang.annotation.RetentionPolicy java.lang.annotation.ElementType
以下转载: ================================================ 在 JBoss Seam 使用 AOP 作 logging / auditing
使用JBoss Seam2.0 ,我們可以利用 Annotation 和 Interceptor 去作 AOP 去減低組件間的耦合。最適合用來處理一些非核心、但容易分佈各處的功能。
目標 我們的軟件需要記錄登入用戶後的工作以作審計用途。系統有很多的組件,每個組件有若干的功能。當用戶使用某功能的時候,我們希望可以記錄該用戶的資料、使用時間、哪一個功能、影響了甚麼資料等等。
第一個答案 最簡單的作法就是寫一個獨立的 Audit 組件,再在每個功能之後使用它:
FundHome.java
@Name("fundHome") publicclassFundHomeextendsEntityHome<Fund>{ publicStringupdate(){ Stringresult =null; try{ // update data // .... }finally{ audit.log("update user account: #0, #1, #2", currentUser, oldFund, newFund); } returnresult; } // ... }
雖然這可以解決問題,然而這樣有關 Audit 的邏輯就混在業務邏輯之中。如果有六十個這種要記錄的功能,就要重覆這種沒個性的代碼 60 次,真痛苦,更別說當中可能有人為出錯和其後果了。本著 DRY (Dont Repeat Yourself) 的精神,我們可以用 Seam 的 Interceptor 去簡化這工作。
Seam Interceptor EJB 3.0 有 interceptor 的標準。任何 class 只要在其 method 上加上 @AroundInvoke 的標記,Container 就會知道這是個 interceptor。想在執行某動作的前後執行這 interceptor,只需在目標方法前加入 @Interceptors 標記以及 interceptor 的 class。
AuditInterceptor.java
@Name("auditInterceptor") publicclassAuditInterceptor{ protectedstaticLog log = Logging.getLog(AuditInterceptor.class); @AroundInvoke publicObjectaudit(InvocationContext context)throwsException{ Objectresult =null; try{ result = context.proceed(); }finally{ User user =(User)Contexts.getSessionContext().get("user"); log.info("[#0][#1][#2]", context.getClass().getName(), context.getMethod().getName(), user==null? "not logged in": user.getLogin()); } returnresult; } }
FundHome.java
@Name("fundHome") publicclassFundHomeextendsEntityHome<Fund>{ @Interceptors(AuditInterceptor.class) publicStringupdate(){ // original update data // .... } }
注意 Contexts.getSessionContext().get(”user”),這是 Seam 去存取 Session Context 的方法。。Interceptor 可以直接存取所有目標的 context,這已足夠處理大部份的應用了。就這樣只需加一個 annotation 就可以做到原本的效果,是否比較漂亮?
EJB3.0 還支援用在 ejb-jar.xml 中定義 Interceptors,JBoss Seam 本身就是用 Interceptor 去運作的:
ejb-jar.xml
<?xmlversion="1.0"encoding="UTF-8"?> <ejb-jarxmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd" version="3.0"> <interceptors> <interceptor> <interceptor-class>org.jboss.seam.ejb.SeamInterceptor</interceptor-class> </interceptor> </interceptors> <assembly-descriptor> <interceptor-binding> <ejb-name>*</ejb-name> <interceptor-class>org.jboss.seam.ejb.SeamInterceptor</interceptor-class> </interceptor-binding> </assembly-descriptor> </ejb-jar>
Meta-Annotation as Interceptor Seam Interceptors 基於 EJB3 之上再加了一些改良。首先你不單可以在 EJB3 中用 Seam Interceptor,Seam 讓普通的 JavaBean 也可以有同樣功能。 其次,進一步 DRY ,Seam 支援用 meta-annotation 去定義 class level interceptor:
Auditor.java
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Interceptors(AuditInterceptor.class) public@interfaceAuditor{}
FundHome.java
@Name("fundHome") @Auditor publicclassFundHomeextendsEntityHome<Fund>{ @Interceptors(AuditInterceptor.class) publicStringupdate(){ // original update data // .... } }
這就等於在 FundHome 中加入 class level interceptor: @Interceptors(AuditInterceptor.class),除了省了打幾個字外,這還讓源碼更加易讀。(注:這個似乎只在 EJB3 才行,在 JavaBean 中要稍為改動,詳情請參考這篇。http://www.seamframework.org/12534.lace)
加入更多自定的資料 由於 Audit 需要監察很多不同功能,怎樣用一個 Interceptor 處理和記錄不同的資料呢?比如新增用戶時我想知道新增的帳戶名稱、轉賬時想知道過數的銀碼等等。按 Seam 的思路,我會用另一個 Annotation 去定義這些資料:
AuditableAction.java
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public@interfaceAuditableAction{ Stringdescription()default""; Stringmodule()default"Unknown"; Stringaction()default"Unknown"; }
更新後的 AuditInterceptor.audit() 方法
@AroundInvoke publicObjectaudit(InvocationContext context)throwsException{ Objectresult =null; try{ result = context.proceed(); }finally{ if(context.getMethod().isAnnotationPresent(AuditableAction.class)){ ActionLogHome actionLogHome =(ActionLogHome)Component.getInstance("actionLogHome"); AuditableAction auditableAction = context.getMethod().getAnnotation(AuditableAction.class); Interpolator interpolator =(Interpolator)Component.getInstance("org.jboss.seam.core.interpolator"); ActionLog actionLog = actionLogHome.getInstance();
User user =(User)Contexts.getSessionContext().get("user"); Stringmodule = StringUtils.left(auditableAction.module(),64); Stringaction = StringUtils.left(auditableAction.action(),64); if(StringUtils.isNotEmpty(auditableAction.details())){ Stringdetails = interpolator.interpolate(auditableAction.details()); actionLog.setDetails(details); } actionLog.setModule(module); actionLog.setAction(action); actionLog.setUser(user); actionLogHome.persist(); } } returnresult; }
ActionLog 是個 Entity Bean。 ActionLogHome 是標源的 EntityHome ,是生產 ActionLog 的地方。
Interpolator 是 Seam 的標準組件,可以 evaluate Seam 的 EL Expression。 FundHome.java
@Name("fundHome") @Auditor publicclassFundHomeextendsEntityHome<Fund>{ @AuditableAction(module="Fund", action="Update", details="fund.id=#{fundHome.instance.id}") publicStringupdate(){ // ... update logic ... } }
利用 annotation 我們可以定義任何的資料,包括 EL Expression。以上的例子就讓我們可以讓 log 記錄獨立的訊息,在 log 前會把 EL Expression 解讀回我們想要的資料。
結語 剛才我們利用 Seam Interceptor 去處理分佈各處的非核心功能。比起用 AspectJ 用另一個檔案和另一種語法去改動程式, Seam Interceptor 較易使用和維護。然而要注意的是 Interceptor 有相當的開銷,要小心過度使用帶來的性能問題。
|