轉自:http://wb284551926.iteye.com/blog/1887650html
最近新項目要啓動,在搭建項目基礎架構的時候,想要加入日誌功能和執行性能監控的功能,想了不少的想法,最後仍是想到用spring的aop功能,一是不須要寫那麼多的重複代碼,二是靈活好用,好了如今就講講這個aop功能的使用。(須要的jar包:spring.jar、asm-2.2.3.jar、asm-commons-2.2.3.jar、aopalliance.jar、aspectjweaver.jar、cglib-nodep-2.1_3.jar,能夠忽略版本號)java
1.創建一個切入類,包括doBefore、doAround、doAfter、doThrowing等方法。node
- package com.util;
-
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.ProceedingJoinPoint;
-
- public class TestAspect {
- public void doAfter(JoinPoint jp) {
- System.out.println("log Ending method: "
- + jp.getTarget().getClass().getName() + "."
- + jp.getSignature().getName());
- }
-
- public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
- long time = System.currentTimeMillis();
- Object retVal = pjp.proceed();
- time = System.currentTimeMillis() - time;
- System.out.println("process time: " + time + " ms");
- return retVal;
- }
-
- public void doBefore(JoinPoint jp) {
- System.out.println("log Begining method: "
- + jp.getTarget().getClass().getName() + "."
- + jp.getSignature().getName());
- }
-
- public void doThrowing(JoinPoint jp, Throwable ex) {
- System.out.println("method " + jp.getTarget().getClass().getName()
- + "." + jp.getSignature().getName() + " throw exception");
- System.out.println(ex.getMessage());
- }
- }
2.在spring的配置文件中加入aop配置,proxy-target-class="true"紅色部分*注意*,不加入這個,action是攔截不到的,切記spring
com.neau.project.*.*.*.*(..))" 這裏主要注意的是express
例如你的包結構是com.xxx.action.actionclass 你須要這樣寫才能攔截到 com.xxx.action.actionclass.* 架構
最後的.*是表明actionclass中的方法。下面有這個配置的具體說明模塊化
若是要放行一些方法,好比get/set方法能夠用 and 關鍵字,例如性能
execution(* com.neau.project.*.*.*.*(..)) and !execution(* com.neau.project.*.*.*.get*(..)) and !execution(* com.neau.project.*.*.*.set*(..))this
- <aop:config <span style="color: #ff0000;">proxy-target-class="true"> </span>
- <aop:aspect id="TestAspect" ref="aspectBean">
-
- <aop:pointcut id="AOPService"
- expression="execution(* <span style="color: #ff0000;">com.neau.project.*.*.*.*</span>(..))" /> <aop:before pointcut-ref="AOPService" method="doBefore"/>
- <aop:after pointcut-ref="AOPService" method="doAfter"/>
- <aop:around pointcut-ref="AOPService" method="doAround"/>
- <aop:after-throwing pointcut-ref="AOPService" method="doThrowing" throwing="ex"/>
- </aop:aspect>
- </aop:config>
- <bean id="aspectBean" class="com.util.TestAspect" />
3.啓動項目,執行spa
訪問action
- log Begining method: com.neau.project.logon.action.LogonAction.test
- 2013-06-16 15:31:56,737 - {pstm-100001} Executing Statement: select count(*) from rdtteam t
- 2013-06-16 15:31:56,737 - {pstm-100001} Parameters: []
- 2013-06-16 15:31:56,737 - {pstm-100001} Types: []
- 2013-06-16 15:31:56,749 - {pstm-100004} Executing Statement: select t.* from rdtteam t limit ?,?
- 2013-06-16 15:31:56,749 - {pstm-100004} Parameters: [0, 20]
- 2013-06-16 15:31:56,749 - {pstm-100004} Types: [java.lang.Integer, java.lang.Integer]
- log Ending method: com.neau.project.logon.action.LogonAction.test
- process time: 261 ms
- log Begining method: com.neau.project.logon.action.LogonAction.getRdtTeamPage
- log Ending method: com.neau.project.logon.action.LogonAction.getRdtTeamPage
- process time: 0 ms
- log Begining method: com.neau.project.logon.action.LogonAction.get_rdtTeamList
- log Ending method: com.neau.project.logon.action.LogonAction.get_rdtTeamList
- process time: 0 ms
aop就這樣實現了攔截action,簡單吧,簡單的工做實現大功能是我等苦逼開發者的目標。
偷點別的寫好的吧。
《Spring參考手冊》中定義瞭如下幾個AOP的重要概念,結合以上代碼分析以下:
- 切面(Aspect) :官方的抽象定義爲「一個關注點的模塊化,這個關注點可能會橫切多個對象」,在本例中,「切面」就是類TestAspect所關注的具體行爲,例如,AServiceImpl.barA()的調用就是切面TestAspect所關注的行爲之一。「切面」在ApplicationContext中<aop:aspect>來配置。
- 鏈接點(Joinpoint) :程序執行過程當中的某一行爲,例如,AServiceImpl.barA()的調用或者BServiceImpl.barB(String _msg, int _type)拋出異常等行爲。
- 通知(Advice) :「切面」對於某個「鏈接點」所產生的動做,例如,TestAspect中對com.spring.service包下全部類的方法進行日誌記錄的動做就是一個Advice。其中,一個「切面」能夠包含多個「Advice」,例如TestAspect
- 切入點(Pointcut) :匹配鏈接點的斷言,在AOP中通知和一個切入點表達式關聯。例如,TestAspect中的全部通知所關注的鏈接點,都由切入點表達式execution(* com.spring.service.*.*(..))來決定
- 目標對象(Target Object) :被一個或者多個切面所通知的對象。例如,AServcieImpl和BServiceImpl,固然在實際運行時,Spring AOP採用代理實現,實際AOP操做的是TargetObject的代理對象。
- AOP代理(AOP Proxy) 在Spring AOP中有兩種代理方式,JDK動態代理和CGLIB代理。默認狀況下,TargetObject實現了接口時,則採用JDK動態代理,例如,AServiceImpl;反之,採用CGLIB代理,例如,BServiceImpl。強制使用CGLIB代理須要將
<aop:config>
的 proxy-target-class
屬性設爲true
通知(Advice)類型
- 前置通知(Before advice) :在某鏈接點(JoinPoint)以前執行的通知,但這個通知不能阻止鏈接點前的執行。ApplicationContext中在<aop:aspect>裏面使用<aop:before>元素進行聲明。例如,TestAspect中的doBefore方法
- 後通知(After advice) :當某鏈接點退出的時候執行的通知(不管是正常返回仍是異常退出)。ApplicationContext中在<aop:aspect>裏面使用<aop:after>元素進行聲明。例如,TestAspect中的doAfter方法,因此AOPTest中調用BServiceImpl.barB拋出異常時,doAfter方法仍然執行
- 返回後通知(After return advice) :在某鏈接點正常完成後執行的通知,不包括拋出異常的狀況。ApplicationContext中在<aop:aspect>裏面使用<after-returning>元素進行聲明。
- 環繞通知(Around advice) :包圍一個鏈接點的通知,相似Web中Servlet規範中的Filter的doFilter方法。能夠在方法的調用先後完成自定義的行爲,也能夠選擇不執行。ApplicationContext中在<aop:aspect>裏面使用<aop:around>元素進行聲明。例如,TestAspect中的doAround方法。
- 拋出異常後通知(After throwing advice) : 在方法拋出異常退出時執行的通知。 ApplicationContext中在<aop:aspect>裏面使用<aop:after-throwing>元素進行聲明。例如,TestAspect中的doThrowing方法。
切入點表達式
- 一般狀況下,表達式中使用」execution「就能夠知足大部分的要求。表達式格式以下:
- execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
modifiers-pattern:方法的操做權限
ret-type-pattern:返回值
declaring-type-pattern:方法所在的包
name-pattern:方法名
parm-pattern:參數名
throws-pattern:異常
其中,除ret-type-pattern和name-pattern以外,其餘都是可選的。上例中,execution(* com.spring.service.*.*(..))表示com.spring.service包下,返回值爲任意類型;方法名任意;參數不做限制的全部方法。
能夠經過args來綁定參數,這樣就能夠在通知(Advice)中訪問具體參數了。例如,<aop:aspect>配置以下
- <aop:config>
- <aop:aspect id="TestAspect" ref="aspectBean">
- <aop:pointcut id="businessService"
- expression="execution(* com.spring.service.*.*(String,..)) and args(msg,..)" />
- <aop:after pointcut-ref="businessService" method="doAfter"/>
- </aop:aspect>
- </aop:config>
TestAspect的doAfter方法中就能夠訪問msg參數,但這樣以來AService中的barA()和BServiceImpl中的barB()就再也不是鏈接點,由於execution(* com.spring.service.*.*(String,..))只配置第一個參數爲String類型的方法。其中,doAfter方法定義以下:
- public void doAfter(JoinPoint jp,String msg)
任何通知(Advice)方法能夠將第一個參數定義爲 org.aspectj.lang.JoinPoint
類型。JoinPoint
接口提供了一系列有用的方法, 好比 getArgs()
(返回方法參數)、getThis()
(返回代理對象)、getTarget()
(返回目標)、getSignature()
(返回正在被通知的方法相關信息)和 toString()
(打印出正在被通知的方法的有用信息。
warning no match for this type name: com.zrm.service [Xlint:invalidAbsoluteTypeName]
<aop:config>
<!-- 切入點 -->
<aop:pointcut id="gkServicePct" expression="execution(* com.zrm.service.*(..))" />
<aop:advisor pointcut-ref="gkServicePct" advice-ref="gkTxAdvice" order="0" />
</aop:config>
出現warning no match for this type name: com.zrm.service [Xlint:invalidAbsoluteTypeName]錯誤
配置事務時,必定注意expression="execution(* com.zrm.service.*(..))" 應該爲
expression="execution(* com.zrm.service.*.*(..))" ,這樣,切點才定位到方法上了。
感謝:http://pandonix.iteye.com/blog/336873
感謝:http://hi.baidu.com/gglzf4/item/a58624e2f962d113585dd832
感謝:http://bbs.csdn.net/topics/350180595 11樓的同志
感謝:http://blog.sina.com.cn/s/blog_5f53615f0101ijdl.html
看了那麼童鞋的經驗讓我少走了很多彎路,看來我就是吃百家飯長大孩紙的命,傷不起啊。苦逼的開發者傷不起啊。