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 }
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 }