在Spring中,最經常使用的AOP框架是AspectJ,使用AspectJ實現AOP有2種方式:spring
基於註解的聲明式AspectJexpress
一、在項目中添加包spring-aspects.jar(spring自帶)、aspectjweaver.jar(須要本身下載添加)。app
二、新建包user,包下新建類User框架
1 public class User{ 2 public void addUser(){ 3 System.out.println("正在添加用戶"); 4 System.out.println("添加用戶成功"); 5 } 6 }
User可實現接口。函數
三、新建包my_aspect,包下新建切面類MyAspect。注意是新建Class,不是新建Aspect。測試
1 public class MyAspect { 2 //前置通知 3 public void myBefore(){ 4 System.out.println("正在檢查權限"); 5 System.out.println("權限已夠"); 6 } 7 8 //後置通知 9 public void myAfterReturning(){ 10 System.out.println("正在記錄日誌"); 11 System.out.println("日誌記錄完畢"); 12 } 13 14 //環繞通知,同時在先後加強。 15 public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { 16 myBefore(); 17 Object object=proceedingJoinPoint.proceed(); 18 myAfterReturning(); 19 return object; 20 } 21 22 //異常通知 23 public void myAfterThrowing(Throwable e){ 24 System.out.println("出錯了:"+e.getMessage()); 25 } 26 27 //最終通知 28 public void myAfter(){ 29 System.out.println("正在釋放資源"); 30 System.out.println("資源釋放完畢"); 31 } 32 }
根據須要,寫通知。函數名是自定義的,但不能使用aspectj的關鍵字。spa
可以使用形參 JoinPoint joinPoint ,經過 joinPoint.getSignature().getName() 獲取加強的方法名。代理
四、在xml中配置日誌
<!--目標類--> <bean id="user" class="user.User" /> <!--切面類--> <bean id="myAspect" class="my_aspect.MyAspect" /> <!--AOP配置--> <aop:config> <!--配置切面,一個aspect配置一個切面--> <aop:aspect ref="myAspect"> <!--配置全局切入點--> <aop:pointcut id="myPointCut" expression="execution(* user.*.*(..))" /> <!--配置要使用的通知--> <aop:before method="myBefore" pointcut-ref="myPointCut" /> <aop:after-returning method="myAfterReturning" pointcut="execution(void user.User.addUser())" /> </aop:aspect> </aop:config>
<aop:pointcut />配置的是全局切入點(全部通知均可引用),可在通知中使用pointcut-ref屬性引用。也能夠在單個通知中使用pointcut屬性配置本身的切入點。
method屬性指定這個通知對應的切面類中的方法。若是方法使用了參數JoinPoint(切入點),不用額外傳參,pointcut/point-ref傳遞的就是切入點。
切入點指的就是目標方法。
<aop:before method="myBefore" pointcut-ref="myPointCut" />
<aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" />
後置通知可以使用returning屬性指定指定一個參數,這個參數表示目標方法的返回值,會被傳遞給後置通知的方法。returning屬性只有後置通知能使用。
<aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="returnVal" />
<aop:around method="myAround" pointcut-ref="myPointCut" />
環繞方法的參數 ProceedingJoinPoint proceedingJoinPoint 是JoinPoint的子類,不用額外傳參。
若是同時使用前置/後置通知、環繞通知,則先執行環繞前,再執行前置(若是有),執行目標方法,執行後置(若是有),而後執行環繞後。
<aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>
異常方法須要一個參數 Throwable e ,須要用throwing屬性傳遞參數。
異常通知在目標方法發生異常、拋出異常時自動執行,throwing表示的就是目標方法拋出的異常。
目標方法發生異常時,會先執行異常方法,而後在控制檯打印錯誤信息。
<aop:after method="myAfter" pointcut-ref="myPointCut" />
最終通知是在目標方法執行後、後置方法執行後/環繞方法執行後(若是有),才執行的。
若是以前的代碼(目標方法)發生異常或者被異常停止,後置通知以及環繞通知的後半部分是不會執行的。
最終通知,就算前面代碼發生異常、被異常停止,也會執行,除非退出JVM。因此常在最終方法中作一些斷開鏈接、關閉資源的操做。
五、新建包test,包下新建測試類Test
1 public class Test { 2 public static void main(String[] args) { 3 ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml"); 4 User user=applicationContext.getBean("user", User.class); 5 user.addUser(); 6 } 7 }
基於代理類的AOP很繁瑣,基於xml的聲明式AspectJ便捷不少,但若是要配置的切面不少,xml文件會很臃腫。基於註解的聲明式AspectJ可解決此問題。
基於註解的聲明式AspectJ是最經常使用的,簡便、xml文件也比較簡潔。
一、在項目中添加包spring-aspects.jar(spring自帶)、aspectjweaver.jar(須要本身下載添加)。
二、新建包user,包下新建類User(可實現接口)
1 @Repository("user") 2 public class User{ 3 public void addUser(){ 4 System.out.println("正在添加用戶"); 5 System.out.println("添加用戶成功"); 6 } 7 }
本來要在xml中配置目標類的Bean,這裏使用註解自動裝配。@Repository("user")是Dao層的註解,由於添加用戶通常要操做數據庫,這裏只是寫了一個模擬。
咱們顯式指定Bean的id/name爲user,其實@Repository默認id/name就是類名的camel寫法。
三、新建包my_aspect,包下新建切面類MyAspect
1 @Aspect 2 @Component 3 public class MyAspect { 4 //全局切入點 5 @Pointcut("execution(void user.User.*())") 6 private void myPointCut(){} 7 8 //前置通知 9 @Before("myPointCut()") 10 public void myBefore(){ 11 System.out.println("正在檢查權限"); 12 System.out.println("權限已夠"); 13 } 14 15 //後置通知 16 @AfterReturning("execution(void user.User.*())") 17 public void myAfterReturning(){ 18 System.out.println("正在記錄日誌"); 19 System.out.println("日誌記錄完畢"); 20 } 21 22 //環繞通知,同時在先後加強。 23 public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { 24 myBefore(); 25 Object object=proceedingJoinPoint.proceed(); 26 myAfterReturning(); 27 return object; 28 } 29 30 //異常通知 31 public void myAfterThrowing(Throwable e){ 32 System.out.println("出錯了:"+e.getMessage()); 33 } 34 35 //最終通知 36 public void myAfter(){ 37 System.out.println("正在釋放資源"); 38 System.out.println("資源釋放完畢"); 39 40 } 41 }
本來要在xm中配置切面Bean,這裏使用@Aspect(使用aspectj配置,至關於<aop:congif>元素)、 @Component(配置爲Bean,至關於<bean>元素) 2個註解配置。
須要使用註解把要使用的方法標註爲對應的通知方法,須要指定切入點。
可先配置全局切入點:
//全局切入點 @Pointcut("execution(void user.User.*())") private void myPointCut(){}
而後在註解中經過 "方法名()" 引用:
//前置通知 @Before(value = "myPointCut()")
若是隻配置切入點這一個參數,可簡寫:
//前置通知 @Before("myPointCut()")
也能夠自行配置:
//後置通知 @AfterReturning("execution(void user.User.*())")
異常通知比較特殊,須要傳遞一個異常參數:
//異常通知 @AfterThrowing(value = "myPointCut()",throwing = "e")
只有使用了註解標註的方法纔會做爲通知自動調用。
其實就是把xml中的配置轉換爲相應的註解配置。
四、在xml中配置
<!--啓用Spring註解,指定使用了註解的包,有多個包時逗號分隔-->
<context:component-scan base-package="user,my_aspect" />
<!--啓用AspectJ的註解-->
<aop:aspectj-autoproxy />
五、新建包test,包下新建測試類Test
1 public class Test { 2 public static void main(String[] args) { 3 ApplicationContext applicationContext=new ClassPathXmlApplicationContext("applicationContext.xml"); 4 User user=applicationContext.getBean("user", User.class); 5 user.addUser(); 6 } 7 }