close

  AOP主要用於Logging、Audit、Profiler、認證授權。其中AspectJ較之於Spring AOP和JBoss AOP更負盛名的AOP產品。AspectJ 5則是支援到JDK 5.0版以上,較之前版,完全Java化,包括annotation。但最大的美中不足是,竟無法對Struts1的MappingDispatchAction或DispatchAction進行weave,據http://howsun.blog.sohu.com/106725713.html這篇Blog所寫,DispatchAction和AOP都是採Reflection,所以無法對會採取Reflection的類別再做一次Reflection,真是殘念。但也讓我找到一個偷吃步的絕技,這個留到最後說,先對AspectJ 5 for Spring3 annotation做個入門筆記:

  1. applicationContext.xml的設定:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="
    http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:context="http://www.springframework.org/schema/context"
            xmlns:aop="http://www.springframework.org/schema/aop"
            xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">   
        <context:component-scan base-package="com.foo.bar"/>
        <aop:aspectj-autoproxy proxy-target-class="true"/>
    </beans>

    上述紅字部份為Spring使用AOP時所需,至於底線加粗的部份,設為false是由JDK動態代理,通常用於Transaction,設為true則是強制使用CGLIB。若要用於Struts1的DispatchAction,設為true是必要。

  2. 以下是無縫接合一個碼錶計時來評測method執行時間。粗紅字部份必要annotation,@Component要為<context:component-scan base-ackage="com.foo.bar"/> 所掃瞄,而<aop:aspectj-autoproxy/> 目的則是掃瞄@Aspect

    import org.apache.commons.lang.time.StopWatch;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;

    @Aspect
    @Component
    public class WebLogger {
        private static Logger logger = LoggerFactory.getLogger(WebLogger.class);
       
    @Pointcut("execution(public * com.foo.bar..*.*(..))")
        public void timer() {
            logger.info("call timer!!");
            System.out.println("come in!!");
        }
        @Around("timer()")
        public Object time(ProceedingJoinPoint joinPoint) throws Throwable {
            String clazz = joinPoint.getTarget().getClass().getSimpleName(); 
            String method = joinPoint.getSignature().getName(); 
            StopWatch clock = new StopWatch();
            clock.start(); 
            Object result = joinPoint.proceed();  // Run method
            clock.stop(); 
            String[] params = new String[] { clazz, method, clock.getTime() + "" }; 
            logger.info("[{}] Run [{}] method spent [{}] ms", params);     
            return result;
        }
    }

    @Around 是一個Advice,同時可以在method執行前和執行後做手腳,甚至可不執行method(上例黑色粗體部份);有別於@Before(執行前)和@After(執行後)或是@AfterThrowing、@AfterReturning只有單一情境攔截。而@Pointcut中的execution是最常用的匹配函數,在AspectJ有十數種匹配函數及其語法,茲不贅述。而@Pointcut後接的method就是@Around advice裡的參數,即上面標綠色字體部份。


  可以揭曉怎麼對Struts1的DispatchAction使用AspectJ了。答案是使用Interface,而applicationContext.xml要把proxy-target-class設為true才有作用,設為false會報錯。使用@Pointcut方式如下:

@Pointcut("target(com.foo.bar.action.ILoginAction)")

  也就是Action class除了繼承DispatchAction或MappingDispatchAction之外,還要寫Interface,再把一個個interface全名設到Pointcut的target上。如此多此數舉何用?

  的確多此數舉,卻也強迫提升到系統設計(SD)層次的思維。網頁操作模式都有其Pattern,比如增改刪除,若沒人去規範的話,有的人在DispatchAction用的method name是insert/update/delete/query,也可能有人這麼定名:create/modify/destroy/search,何不一統江湖呢?

  以上面例子而言,印出的Log的長相也令我吃了一驚,如下:

2010/03/14 01:13:04 INFO  [WebLogger.java:41] : [LoginAction] Run [execute] method spent [80] ms

  繼承MappingDispatchAction的LoginAction是實作ILoginAction的login method,但印出卻是execute,這其實是反映了MappingDispatchAction是繼承Action,要是多個method,豈非每個method都叫execute?這確實是好問題,可以從JointPoint.getTarget與getSignNatrure取得instance著手再研究了。

arrow
arrow
    全站熱搜

    Jemmy 發表在 痞客邦 留言(0) 人氣()