在Spring中使用AspectJ實現AOP

 

在Spring中,最經常使用的AOP框架是AspectJ,使用AspectJ實現AOP有2種方式:spring

 

 

基於XML的聲明式AspectJ

一、在項目中添加包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 }

 

 

 

 

 

基於註解的聲明式AspectJ

基於代理類的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 }
相關文章
相關標籤/搜索