low level programmer

« Transfer image to... | Main | EJB3 - TimerService »
星期六 四月 19, 2008

EJB3 - Interceptors

Reference

EJB3 in Action - CH5 - Learning advanced EJB concepts

Description

Interceptor用來定義要在method呼叫時做的事情, 方便做統一logger或validate等操作.

Focal Points

  1. 用@Interceptors指定interceptor, 用@AroundInvoke實做interceptor要做的事情. 如下:
    // @Interceptor放此就是class等級的interceptor, 
    // 則此class呼叫到的每個method都會呼叫該interceptor的method.            
    @Interceptors(ClassLevelAroundInvoke.class) 
    @Stateful
    public class TestStateful implements ITestStateful {
        ...
        // @Interceptors可指定一或多個interceptor(用逗號隔開interceptor class)
        // 指定呼叫此method時的interceptor為TestInterceptor
        // 也可@Interceptor(TestInterceptor.class)
        @Interceptors({TestInterceptor.class, TestInteceptor2.class}) 
        public String addString(String src) {
            status.setName(status.getName() + src);
            return status.getName();
        }
        ...
    }
    
    // addString被呼叫時TestInterceptor.aroundInvokd會被呼叫
    public class TestInterceptor {
    
        @AroundInvoke
        public Object aroundInvoke(InvocationContext invocationContext) 
        throws Exception {
            System.out.println("TestStatelessInterceptor.aroundInvoke is called from:" 
                                + invocationContext.getMethod());
            return invocationContext.proceed();
        }
        
    }
    
    // addString被呼叫時TestInterceptor.aroundInvokd會被呼叫
    public class TestInteceptor2 {
    
        @AroundInvoke
        public Object aroundInvoke(InvocationContext invocationContext) 
        throws Exception {
            System.out.println("TestStatelessInteceptor2.aroundInvoke is called from:" 
                                + invocationContext.getMethod());
            return invocationContext.proceed();
        }
        
    }
    
    // TestStateful的每個method被呼叫時都會呼叫此interceptor
    public class ClassLevelAroundInvoke {
    
        @AroundInvoke
        public Object aroundInvoke(InvocationContext invocationContext) 
        throws Exception {
            System.out.println("ClassLevelAroundInvoke.aroundInvoke is called from:" 
                                + invocationContext.getTarget() + " method:" 
                                + invocationContext.getMethod());
            return invocationContext.proceed();
        }
        
    }
            
  2. 如果要指定一個每個class的每個method都呼叫的interceptor, 要在ejb-jar.xml指定.
    < !-- 先註冊interceptor -- >
    < interceptors>
        < interceptor>
            < interceptor-class>interceptors.DefaultInterceptor< /interceptor-class>
        < /interceptor>
    < /interceptors>
    < !-- 再指定該interceptor為DefaultInterceptor -- >
    < assembly-descriptor>
        < interceptor-binding>
            < ejb-name>*< /ejb-name>
            < interceptor-class>interceptors.DefaultInterceptor< /interceptor-class>
        < /interceptor-binding>
    < /assembly-descriptor>
    
    public class ClassLevelAroundInvoke {
    
        @AroundInvoke
        public Object aroundInvoke(InvocationContext invocationContext) 
        throws Exception {
            System.out.println("ClassLevelAroundInvoke.aroundInvoke is called from:" 
                                + invocationContext.getTarget() + " method:" 
                                + invocationContext.getMethod());
            return invocationContext.proceed();
        }
        
    }
            
  3. interceptor的優先順序是: DefaultInterceptor -> Class Interceptor -> Method Interceptor
  4. 同等級的interceptor優先順序就是指定interceptor的順序
  5. 同等級的interceptor若同時在程式與XML都有設定, 則程式設定的優先.
  6. 若在XML與程式中指定一樣的interceptor則該interceptor會跑兩次
  7. 可用在ejb-jar.xml的interceptor-order指定順序, 指定順序之後程式指定的interceptor就會失效(失效的結果是實做發現的, 文件上沒看到明確的說這件事情)
    < interceptors>
        < interceptor>
            < interceptor-class>interceptors.TestInterceptor< /interceptor-class>
        < /interceptor>
        < interceptor>
            < interceptor-class>interceptors.TestInterceptor2< /interceptor-class>
        < /interceptor>
    < /interceptors>
    < assembly-descriptor>
        < interceptor-binding>
            < ejb-name>TestInterceptorOrder< /ejb-name>
            < interceptor-order>
                < interceptor-class>interceptors.TestInterceptor2< /interceptor-class>
                < interceptor-class>interceptors.TestInterceptor< /interceptor-class>
            < /interceptor-order>
        < /interceptor-binding>
    < /assembly-descriptor>
            
    若指定interceptor-order後再指定method level的interceptor, 則以method-level的interceptor-order為主, 原來的interceptor-order會失效(實作發現的, 文件上沒明說)
    < interceptor-binding>
        < ejb-name>TestInterceptorOrder< /ejb-name>
        < interceptor-order>
            < interceptor-class>interceptors.DefaultInterceptor< /interceptor-class>
            < interceptor-class>interceptors.XMLAssignedInterceptor< /interceptor-class>
            < interceptor-class>interceptors.DefaultInterceptor< /interceptor-class>
        < /interceptor-order>
        < method>
            < method-name>showResult< /method-name>
        < /method>
    < /interceptor-binding>
    < interceptor-binding>
        < ejb-name>TestInterceptorOrder< /ejb-name>
        < interceptor-order>
            < interceptor-class>interceptors.TestInterceptor2< /interceptor-class>
            < interceptor-class>interceptors.TestInterceptor< /interceptor-class>
        < /interceptor-order>
    < /interceptor-binding>
            
  8. 在class或method上指定@ExcludeDefaultInterceptors使指定的class或method不會呼叫interceptor
    在method指定@ExcludeClassInterceptors使指定的method不會呼叫interceptor
    @ExcludeDefaultInterceptors //在這指定使此class所有method都不會呼叫DefaultInterceptor
    @Interceptors(TestInterceptor.class)
    @Stateless
    public class TestDI implements ITestDI {
    
        @ExcludeDefaultInterceptors // 在者指定使此method不會呼叫DefaultInterceptor
        @ExcludeClassInterceptors // 指定此method不會呼叫Class interceptor
        public String showDIResult() {
            StringBuffer sb = new StringBuffer();
            sb.append("how to inject mystringvalue from env-entry?:" 
                        + mystringvalueKK);
            sb.append("TimerService:" + timerService);
            sb.append("dataSourceFromField:" + dataSourceFromField.toString());
            sb.append("mailSessionFromSetter:" + mailSessionFromSetter.toString());
            sb.append("mailSessionFromInitialContext:" 
                        + mailSessionFromInitialContext.toString());
            sb.append("mailSessionFromUtils:" + mailSessionFromUtils.toString());
            return "DI Result:" + sb.toString();
        }
        
    }            
            
  9. 一個interceptor一此只能一個method當作around invoke method
  10. around invoke method不能是business method, 也就是不能定在business interface.
  11. around invoke method要在method上指定@AroundInvoke
  12. around invoke method必須符合此pattern: Object < MEHOD >(InvocationContext) throws Exception
  13. around invoke method回傳InvocationContext.proceed告訴container能繼續往下執行其他的interceptor或business method. 若沒有回傳InvocationContext.proceed就會導致interceptor中斷且不會執行business method. 因而能用interceptor控制business method的流程. 例如可在interceptor做validate, 沒通過validate就丟出exception不繼續執行.
  14. InvocationContext.getMethod在會回傳呼叫此interceptor時候的method, 若為business method就會回傳該method, 若為lifecycle callback就會回傳null.
  15. InvocationContext.getParameters與InvocationContext.setParameters可用來取得傳入method的參數並可修改
  16. InvocationContext.getContextData用來取得一個map存放interceptor間能共同使用的資料
  17. interceptor也有自己的生命週期, 和stateless session bean一樣是@PostConstruct和@PreDestroy
    注意interceptor around invoke method的pattern是 Object < METHOD>(InvocationContext) throws Exception
    interceptor lifecycle callback pattern則是 void < METHOD>(InvocationContext)
    就是interceptor around invoke method有回傳Object與丟Exception, interceptor lifecycle callback則無.
    lifecycle callback仍需要呼叫InvocationContext.proceed()來讓interceptor接起來(猜是因為用Chain Of Responsibility?).
    public class TestInterceptor {
    
        @PostConstruct
        public void postConstructInterceptor(InvocationContext invocationContext) {
            try {
                System.out.println("TestInterceptor post construct:" 
                                    + invocationContext.getMethod());
                invocationContext.proceed();
            } catch (Exception ex) {
                Logger.getLogger(TestInterceptor.class.getName())
                                    .log(Level.SEVERE, null, ex);
            }
        }
        
        @AroundInvoke
        public Object aroundInvoke(InvocationContext invocationContext) 
        throws Exception {
            System.out.println("TestStatelessInterceptor.aroundInvoke is called from:" 
                                + invocationContext.getMethod());
            return invocationContext.proceed();
        }
        
        @PreDestroy
        public void preDestroyInterceptor(InvocationContext invocationContext) {
            try {
                System.out.println("TestInterceptor preDestroy:" 
                                    + invocationContext.getMethod());
                invocationContext.proceed();
            } catch (Exception ex) {
                Logger.getLogger(TestInterceptor.class.getName())
                                    .log(Level.SEVERE, null, ex);
            }
        }
    }            
            

迴響:

發表迴響:
  • HTML 語法: 關閉