SpringBoot之集成Spring AOP

Posted Titter_Z

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot之集成Spring AOP相关的知识,希望对你有一定的参考价值。

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 请求 : [email protected] , HttpSession : [email protected] 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         请求 : [email protected] ,  HttpSession : [email protected]
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         请求 : [email protected] ,  HttpSession : [email protected]
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         请求 : [email protected] ,  HttpSession : [email protected]
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的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot集成Redis实现缓存处理(Spring AOP实现)

springboot 集成aop模块

Spring Boot 入门:集成 AOP 进行日志管理

Spring全家桶——SpringBoot之AOP详解

SpringBoot之AOP使用

springboot集成AOP管理日志