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做個入門筆記:
- 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是必要。
- 以下是無縫接合一個碼錶計時來評測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著手再研究了。
留言列表