1. 程式人生 > >SpringBoot之集成Spring AOP

SpringBoot之集成Spring AOP

切面 pen ogl rac return owa collect web ali

SpringBoot集成ApringAOP步驟如下:

1.導包

                <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>


		<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
                <!-- com.google.common.collect.Maps 依賴-->
		<dependency>
			<groupId>com.google.guava</groupId>
			<artifactId>guava</artifactId>
			<version>24.1-jre</version>
		</dependency>

		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.43</version>
		</dependency>        

2.這裏主要介紹通知的類型有:前置通知、後置返回通知、後置最終通知、後置異常通知、環繞通知;詳細如下:

-------------------- 準備工作 ---------------------

    a . 創建Aspect切面類;    

 package com.zdj.springboot_aop;

/**
 * Created by Administrator on 2018/3/28.
 */

/**
 * 1.先創建一個Aspect切面類
 */
@Component
@Aspect
public class WebControllerAop {

}

   b. 指定切點

/**
 * 2. 指定切點
 * 匹配com.zdj.springboot_aop.Controller包及其子包下面的所有類的所有方法
 */
    @Pointcut("execution(* com.zdj.springboot_aop.Controller..*.*(..))")
    public  void executeService(){

    }

c. 創建Controller類 處理請求

package com.zdj.springboot_aop.Controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Created by Administrator on 2018/3/28.
 */

@RestController
@RequestMapping("/aop")
public class AopTestController {

}

  2.1 前置通知

2.1.1 配置前置通知

 1    /*
 2      *  01 . 前置通知:方法調用前被調用
 3      */
 4     @Before("executeService()")
 5     public void doBeforeAdvice(JoinPoint joinPoint){//  通過JoinPoint 獲取通知的簽名信息,如目標方法名,目標方法參數信息等
 6         System.out.println("我是前置通知");
 7         Object[] obj=joinPoint.getArgs();//獲取目標方法的參數信息
 8         joinPoint.getThis(); // AOP代理類信息
 9         joinPoint.getTarget(); // 代理的目標對象
10         Signature signature=joinPoint.getSignature(); //  用的最多,通知的簽名
11         System.out.println("代理的方法是 : "+signature.getName()); //  打印 代理的是哪一個方法
12         // AOP 代理的名字
13         System.out.println("AOP 代理的名字 : "+signature.getDeclaringTypeName());
14         signature.getDeclaringType();//  AOP代理類的類(class)信息
15 
16         /*
17           通過RequestContextHolder獲取請求信息,如session 信息 ;
18          */
19         //  獲取RequestAttributes
20         RequestAttributes requestAttributes= RequestContextHolder.getRequestAttributes();
21         //  從requestAttributes中獲取HttpServletRequest信息
22         HttpServletRequest request=(HttpServletRequest)requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
23         //  獲取session信息
24         HttpSession session=(HttpSession)requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);
25 
26         System.out.println("請求 : "+request+" ,  HttpSession : "+session);
27         Enumeration<String> enumerations=request.getParameterNames();
28 //        Map<String,String> parameterMaps=new HashMap<>();
29         Map<String,String> parameterMaps= Maps.newHashMap();
30         while(enumerations.hasMoreElements()){
31             String parameter=enumerations.nextElement();
32             parameterMaps.put(parameter,request.getParameter(parameter));
33         }
34 
35 //        String str=JSON.toJSONString(parameterMaps);
36         String str= JSON.toJSONString(parameterMaps);//   alibaba.fastjson
37         if(obj.length>0){
38             System.out.println("請求參數信息為 : "+ str );
39         }
40 
41     }

註:

關於調用 JoinPoint 和 RequestContextHolder。 通過JoinPoint可以獲得通知的簽名信息,如目標方法名、目標方法參數信息等。 通過RequestContextHolder來獲取請求信息,Session信息。

    2.2.2 在Controller類裏添加一個請求處理方法來測試一下前置通知:

 1     @RequestMapping("/testBeforeService.do")
 2     public String testBeforeService(String key ,String value){
 3         return "key : "+key+ ", value : "+value;
 4         /*
 5         url:  http://localhost:8080/aop/testBeforeService.do?key=zdj&value=123
 6         執行結果 : 
       我是前置通知
7 代理的方法是 : testBeforeService 8 AOP 代理的名字 : com.zdj.springboot_aop.Controller.AopTestController 9 請求 : org.apache.catalina.connector.RequestFacade@4f8cf74e , HttpSession : org.apache.catalina.session.StandardSessionFacade@7b37a638 10 請求參數信息為 : {"value":"123","key":"zdj"} 11 */ 12 }

  2.2 後置返回通知

    2.2.1 配置後置返回通知的代碼如下:

 1      /**
 2      * 02  .後置返回通知
 3      * 需要註意:
 4      *      如果第一個參數是JoinPoint,則第二個參數是返回值的信息
 5      *      如果參數中的第一個不是JoinPoint,則第一個參數是returning中對應的參數,
 6      *    returning 限定了只有目標方法返回值與通知方法相應參數類型時才能
 7      * 執行後置返回通知,否則不執行;
 8      * 對於returning對應的通知方法參數為Object類型將匹配任何目標返回值
 9      * @param joinPoint
10      * @param keys
11      */
12     @AfterReturning(value="execution(* com.zdj.springboot_aop.Controller..*.*(..))",returning = "keys")
13     public void doAfterReturningAdvice1(JoinPoint joinPoint,Object keys){
14         System.out.println("後置通知執行了!!");
15         System.out.println("第一個後置返回通知的返回值是 :"+keys);
16     }
17 
18     @AfterReturning(value="execution(* com.zdj.springboot_aop.Controller..*.*(..))",returning = "keys",argNames="keys")
19     public void doAfterReturningAdvice2(String keys){ // 通知方法形影參數的類型是String
20         System.out.println("第二個後置返回通知的返回值是 :"+keys);
21     }

    2.2.2 測試後置返回通知

 1 @RequestMapping("/testAfterReturning1.do")
 2     public String testAfterReturning1(String key){
 3         return "key = "+key;
 4         /*
 5             url :  http://localhost:8080/aop/testAfterReturning1.do?key=zdj&value=123
 6             後置通知執行了!!
 7             第一個後置返回通知的返回值是 :key = zdj
 8             第二個後置返回通知的返回值是 :key = zdj
 9          */
10     }
11 
12     @RequestMapping("/testAfterReturning2.do")
13     public Integer testAfterReturning2(Integer key){
14         return key;
15         /*
16             url :  http://localhost:8080/aop/testAfterReturning2.do?key=111222&value=123
17 
18             後置通知執行了!!
19             第一個後置返回通知的返回值是 :111222
20 
21             註 : 因第二個後置通知首參不是JoinPoint,並且相應參數類型是String,而該目標方法的返回值類型是Integer,所以第二個後置通知方法不執行
22 
23          */
24     }

  2.3 後置異常通知

    2.3.1 配置後置異常通知如下:

 1      /**
 2      *  03 . 後置異常通知
 3      *       定義一個名字,該名字用於匹配通知實現方法的一個參數名,當目標方法拋出異常返回後,將把目標方法拋出的異常傳給通知方法;
 4      *  throwing 限定了只有目標方法拋出的異常與通知方法相應參數異常類型時才能執行後置異常通知,否則不執行,
 5      *      對於throwing對應的通知方法參數為Throwable類型將匹配任何異常。
 6      */
 7     @AfterThrowing(value="executeService()",throwing = "exception")
 8     public void doAfterThrowingAdvice(JoinPoint joinPoint,Throwable exception){
 9         // 目標方法名
10         System.out.println(joinPoint.getSignature().getName());
11         if(exception instanceof NullPointerException){
12             System.out.println("發生了空指針異常");
13         }
14     }

    2.3.2 controller測試後置異常通知如下:

 1     @RequestMapping("/testAfterThrowing.do")
 2     public  String testAfterThrowing(String key){
 3         throw new NullPointerException();
 4         /*
 5         url : http://localhost:8080/aop/testAfterThrowing.do?key=zdk&value=123
 6         我是前置通知
 7         代理的方法是 : testAfterThrowing
 8         AOP 代理的名字 : com.zdj.springboot_aop.Controller.AopTestController
 9         請求 : org.apache.catalina.connector.RequestFacade@41b8dcce ,  HttpSession : org.apache.catalina.session.StandardSessionFacade@33c33c37
10         請求參數信息為 : {"value":"123","key":"zdk"}
11         testAfterThrowing
12         發生了空指針異常
13         */
14     }

  2.4 後置最終通知

    2.4.1 配置後置最終通知如下:

1      /**
2      * 04 . 後置最終通知(目標方法只要執行完了就會執行後置通知方法)
3      */
4 
5     @After("executeService()")
6     public void doAfterService(JoinPoint joinPoint){
7         System.out.println("後置最終通知執行了!");
8     }

    2.4.2 controller測試後置最終通知:

 1     @RequestMapping("/testAfter1.do")
 2     public String testAfter1(String key){
 3         throw new NullPointerException();
 4         /*
 5         url: http://localhost:8080/aop/testAfter1.do?key=zdj&value=123
 6         後置最終通知執行了!
 7          */
 8     }
 9 
10     @RequestMapping("/testAfter2.do")
11     public String testAfter2(String key){
12         return key;
13         /*
14         url: http://localhost:8080/aop/testAfter2.do?key=zdj&value=123
15         後置最終通知執行了!
16          */
17     }

  2.5 環繞通知

    2.5.1 配置環繞通知如下:

 1      /**
 2      * 環繞通知:
 3      *   環繞通知非常強大,可以決定目標方法是否執行,什麽時候執行,執行時是否需要替換方法參數,執行完畢是否需要替換返回值。
 4      *   環繞通知第一個參數必須是org.aspectj.lang.ProceedingJoinPoint類型
 5      */
 6     @Around("execution(* com.zdj.springboot_aop.Controller..*.testAround*(..))")
 7     public Object doAroundService(ProceedingJoinPoint proceedingJoinPoint){
 8         System.out.println("環繞通知的目標方法名為 : "+proceedingJoinPoint.getSignature().getName());
 9         try {
10             Object object=proceedingJoinPoint.proceed();
11             return object;
12         } catch (Throwable throwable) {
13             throwable.printStackTrace();
14         }
15         return  null;
16     }

    2.5.2 controller 測試環繞通知如下:

 1     @RequestMapping("/testAroundService.do")
 2     public String testAroundService(String key){
 3         return "環繞通知 : " + key;
 4         /*
 5         url : http://localhost:8080/aop/testAroundService.do?key=1122
 6         環繞通知的目標方法名為 : testAroundService
 7 
 8         當訪問 http://localhost:8080/aop/testAfter2.do?key=1122&value=sjshhjdh,不符合環繞通知的切入規則,所以環繞通知不會執行;
 9          */
10     }

3. 完整代碼如下:

技術分享圖片
  1 package com.zdj.springboot_aop;
  2 
  3 import com.alibaba.fastjson.JSON;
  4 import com.google.common.collect.Maps; // guava   24.1-jar
  5 import org.aspectj.lang.JoinPoint;
  6 import org.aspectj.lang.ProceedingJoinPoint;
  7 import org.aspectj.lang.Signature;
  8 import org.aspectj.lang.annotation.*;
  9 import org.springframework.stereotype.Component;
 10 import org.springframework.web.context.request.RequestAttributes;
 11 import org.springframework.web.context.request.RequestContextHolder;
 12 
 13 //import com.google.common.collect.Maps;
 14 import javax.servlet.http.HttpServletRequest;
 15 import javax.servlet.http.HttpSession;
 16 import java.util.Enumeration;
 17 import java.util.HashMap;
 18 import java.util.Map;
 19 
 20 /**
 21  * Created by Administrator on 2018/3/28.
 22  */
 23 
 24 /**
 25  * 1.先創建一個Aspect切面類
 26  */
 27 @Component
 28 @Aspect
 29 public class WebControllerAop {
 30 
 31 /**
 32  * 2. 指定切點
 33  * 匹配com.zdj.springboot_aop.Controller包及其子包下面的所有類的所有方法
 34  */
 35     @Pointcut("execution(* com.zdj.springboot_aop.Controller..*.*(..))")
 36     public  void executeService(){
 37 
 38     }
 39 
 40     /**
 41      *  01 . 前置通知:方法調用前被調用
 42      */
 43     @Before("executeService()")
 44     public void doBeforeAdvice(JoinPoint joinPoint){//  通過JoinPoint 獲取通知的簽名信息,如目標方法名,目標方法參數信息等
 45         System.out.println("我是前置通知");
 46         Object[] obj=joinPoint.getArgs();//獲取目標方法的參數信息
 47         joinPoint.getThis(); // AOP代理類信息
 48         joinPoint.getTarget(); // 代理的目標對象
 49         Signature signature=joinPoint.getSignature(); //  用的最多,通知的簽名
 50         System.out.println("代理的方法是 : "+signature.getName()); //  打印 代理的是哪一個方法
 51         // AOP 代理的名字
 52         System.out.println("AOP 代理的名字 : "+signature.getDeclaringTypeName());
 53         signature.getDeclaringType();//  AOP代理類的類(class)信息
 54 
 55         /*
 56           通過RequestContextHolder獲取請求信息,如session 信息 ;
 57          */
 58         //  獲取RequestAttributes
 59         RequestAttributes requestAttributes= RequestContextHolder.getRequestAttributes();
 60         //  從requestAttributes中獲取HttpServletRequest信息
 61         HttpServletRequest request=(HttpServletRequest)requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
 62         //  獲取session信息
 63         HttpSession session=(HttpSession)requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);
 64 
 65         System.out.println("請求 : "+request+" ,  HttpSession : "+session);
 66         Enumeration<String> enumerations=request.getParameterNames();
 67 //        Map<String,String> parameterMaps=new HashMap<>();
 68         Map<String,String> parameterMaps= Maps.newHashMap();
 69         while(enumerations.hasMoreElements()){
 70             String parameter=enumerations.nextElement();
 71             parameterMaps.put(parameter,request.getParameter(parameter));
 72         }
 73 
 74 //        String str=JSON.toJSONString(parameterMaps);
 75         String str= JSON.toJSONString(parameterMaps);//   alibaba.fastjson
 76         if(obj.length>0){
 77             System.out.println("請求參數信息為 : "+ str );
 78         }
 79 
 80     }
 81 
 82     /**
 83      * 02  .後置返回通知
 84      * 需要註意:
 85      *      如果第一個參數是JoinPoint,則第二個參數是返回值的信息
 86      *      如果參數中的第一個不是JoinPoint,則第一個參數是returning中對應的參數,
 87      *    returning 限定了只有目標方法返回值與通知方法相應參數類型時才能
 88      * 執行後置返回通知,否則不執行;
 89      * 對於returning對應的通知方法參數為Object類型將匹配任何目標返回值
 90      * @param joinPoint
 91      * @param keys
 92      */
 93     @AfterReturning(value="execution(* com.zdj.springboot_aop.Controller..*.*(..))",returning = "keys")
 94     public void doAfterReturningAdvice1(JoinPoint joinPoint,Object keys){
 95         System.out.println("後置通知執行了!!");
 96         System.out.println("第一個後置返回通知的返回值是 :"+keys);
 97     }
 98 
 99     @AfterReturning(value="execution(* com.zdj.springboot_aop.Controller..*.*(..))",returning = "keys",argNames="keys")
100     public void doAfterReturningAdvice2(String keys){ // 通知方法形影參數的類型是String
101         System.out.println("第二個後置返回通知的返回值是 :"+keys);
102     }
103 
104     /**
105      *  03 . 後置異常通知
106      *       定義一個名字,該名字用於匹配通知實現方法的一個參數名,當目標方法拋出異常返回後,將把目標方法拋出的異常傳給通知方法;
107      *  throwing 限定了只有目標方法拋出的異常與通知方法相應參數異常類型時才能執行後置異常通知,否則不執行,
108      *      對於throwing對應的通知方法參數為Throwable類型將匹配任何異常。
109      */
110     @AfterThrowing(value="executeService()",throwing = "exception")
111     public void doAfterThrowingAdvice(JoinPoint joinPoint,Throwable exception){
112         // 目標方法名
113         System.out.println(joinPoint.getSignature().getName());
114         if(exception instanceof NullPointerException){
115             System.out.println("發生了空指針異常");
116         }
117     }
118 
119 
120     /**
121      * 04 . 後置最終通知(目標方法只要執行完了就會執行後置通知方法)
122      */
123 
124     @After("executeService()")
125     public void doAfterService(JoinPoint joinPoint){
126         System.out.println("後置最終通知執行了!");
127     }
128 
129     /**
130      * 環繞通知:
131      *   環繞通知非常強大,可以決定目標方法是否執行,什麽時候執行,執行時是否需要替換方法參數,執行完畢是否需要替換返回值。
132      *   環繞通知第一個參數必須是org.aspectj.lang.ProceedingJoinPoint類型
133      */
134     @Around("execution(* com.zdj.springboot_aop.Controller..*.testAround*(..))")
135     public Object doAroundService(ProceedingJoinPoint proceedingJoinPoint){
136         System.out.println("環繞通知的目標方法名為 : "+proceedingJoinPoint.getSignature().getName());
137         try {
138             Object object=proceedingJoinPoint.proceed();
139             return object;
140         } catch (Throwable throwable) {
141             throwable.printStackTrace();
142         }
143         return  null;
144     }
145 }
View Code 技術分享圖片
 1 package com.zdj.springboot_aop.Controller;
 2 
 3 import org.springframework.web.bind.annotation.RequestMapping;
 4 import org.springframework.web.bind.annotation.RestController;
 5 
 6 /**
 7  * Created by Administrator on 2018/3/28.
 8  */
 9 
10 @RestController
11 @RequestMapping("/aop")
12 public class AopTestController {
13 
14     @RequestMapping("/testBeforeService.do")
15     public String testBeforeService(String key ,String value){
16         return "key : "+key+ ", value : "+value;
17         /*
18         url:  http://localhost:8080/aop/testBeforeService.do?key=zdj&value=123
19         我是前置通知
20         代理的方法是 : testBeforeService
21         AOP 代理的名字 : com.zdj.springboot_aop.Controller.AopTestController
22         請求 : org.apache.catalina.connector.RequestFacade@4f8cf74e ,  HttpSession : org.apache.catalina.session.StandardSessionFacade@7b37a638
23         請求參數信息為 : {"value":"123","key":"zdj"}
24          */
25     }
26 
27     @RequestMapping("/testAfterReturning1.do")
28     public String testAfterReturning1(String key){
29         return "key = "+key;
30         /*
31             url :  http://localhost:8080/aop/testAfterReturning1.do?key=zdj&value=123
32             後置通知執行了!!
33             第一個後置返回通知的返回值是 :key = zdj
34             第二個後置返回通知的返回值是 :key = zdj
35          */
36     }
37 
38     @RequestMapping("/testAfterReturning2.do")
39     public Integer testAfterReturning2(Integer key){
40         return key;
41         /*
42             url :  http://localhost:8080/aop/testAfterReturning2.do?key=111222&value=123
43 
44             後置通知執行了!!
45             第一個後置返回通知的返回值是 :111222
46 
47             註 : 因第二個後置通知首參不是JoinPoint,並且相應參數類型是String,而該目標方法的返回值類型是Integer,所以第二個後置通知方法不執行
48          */
49     }
50 
51     @RequestMapping("/testAfterThrowing.do")
52     public  String testAfterThrowing(String key){
53         throw new NullPointerException();
54         /*
55         url : http://localhost:8080/aop/testAfterThrowing.do?key=zdk&value=123
56         我是前置通知
57         代理的方法是 : testAfterThrowing
58         AOP 代理的名字 : com.zdj.springboot_aop.Controller.AopTestController
59         請求 : org.apache.catalina.connector.RequestFacade@41b8dcce ,  HttpSession : org.apache.catalina.session.StandardSessionFacade@33c33c37
60         請求參數信息為 : {"value":"123","key":"zdk"}
61         testAfterThrowing
62         發生了空指針異常
63         */
64     }
65 
66     @RequestMapping("/testAfter1.do")
67     public String testAfter1(String key){
68         throw new NullPointerException();
69         /*
70         url: http://localhost:8080/aop/testAfter1.do?key=zdj&value=123
71         後置最終通知執行了!
72          */
73     }
74 
75     @RequestMapping("/testAfter2.do")
76     public String testAfter2(String key){
77         return key;
78         /*
79         url: http://localhost:8080/aop/testAfter2.do?key=zdj&value=123
80         後置最終通知執行了!
81          */
82     }
83 
84     @RequestMapping("/testAroundService.do")
85     public String testAroundService(String key){
86         return "環繞通知 : " + key;
87         /*
88         url : http://localhost:8080/aop/testAroundService.do?key=1122
89         環繞通知的目標方法名為 : testAroundService
90 
91         當訪問 http://localhost:8080/aop/testAfter2.do?key=1122&value=sjshhjdh,不符合環繞通知的切入規則,所以環繞通知不會執行;
92          */
93     }
94 
95 }
View Code

SpringBoot之集成Spring AOP