Spring Boot 整合 AOP
一、示例
1、依赖
1 <dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-web</artifactId> 4 </dependency> 5 <dependency> 6 <groupId>org.springframework.boot</groupId> 7 <artifactId>spring-boot-starter-aop</artifactId> 8 </dependency> 9 <dependency> 10 <groupId>org.aspectj</groupId> 11 <artifactId>aspectjrt</artifactId> 12 </dependency> 13 <dependency> 14 <groupId>org.aspectj</groupId> 15 <artifactId>aspectjweaver</artifactId> 16 </dependency> 17 <dependency> 18 <groupId>cglib</groupId> 19 <artifactId>cglib</artifactId> 20 <version>2.2.2</version> 21 </dependency> 22 <dependency> 23 <groupId>org.springframework</groupId> 24 <artifactId>spring-aop</artifactId> 25 </dependency>
2、Controller
1 package com.aop.aop.controller; 2 3 import org.springframework.web.bind.annotation.RequestMapping; 4 import org.springframework.web.bind.annotation.RequestMethod; 5 import org.springframework.web.bind.annotation.RestController; 6 7 @RestController 8 @RequestMapping("/aopController") 9 public class AopController { 10 11 @RequestMapping(value="/sayHello", method=RequestMethod.GET) 12 public String sayHello(String name){ 13 return "hello" + name; 14 } 15 16 @RequestMapping(value="/sayHello2", method=RequestMethod.GET) 17 public String sayHello2(String name){ 18 return "hello" + name; 19 } 20 }
3、定义切面类
1 package com.aop.aop; 2 3 import org.aspectj.lang.JoinPoint; 4 import org.aspectj.lang.annotation.AfterReturning; 5 import org.aspectj.lang.annotation.Aspect; 6 import org.aspectj.lang.annotation.Before; 7 import org.aspectj.lang.annotation.Pointcut; 8 import org.slf4j.Logger; 9 import org.slf4j.LoggerFactory; 10 import org.springframework.stereotype.Component; 11 import org.springframework.web.context.request.RequestContextHolder; 12 import org.springframework.web.context.request.ServletRequestAttributes; 13 14 import javax.servlet.http.HttpServletRequest; 15 import java.util.Arrays; 16 17 @Aspect 18 @Component 19 public class WebLogAcpect { 20 private Logger logger = LoggerFactory.getLogger(WebLogAcpect.class); 21 22 /** 23 * 定义切入点,切入点为com.aop.aop.controller下的所有函数 24 */ 25 @Pointcut("execution(public * com.aop.aop.controller..*.*(..))") 26 public void webLog(){} 27 28 /** 29 * 前置通知:在连接点之前执行的通知 30 * @param joinPoint 31 * @throws Throwable 32 */ 33 @Before("webLog()") 34 public void doBefore(JoinPoint joinPoint) throws Throwable { 35 // 接收到请求,记录请求内容 36 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); 37 HttpServletRequest request = attributes.getRequest(); 38 39 // 记录下请求内容 40 logger.info("URL : " + request.getRequestURL().toString()); 41 logger.info("HTTP_METHOD : " + request.getMethod()); 42 logger.info("IP : " + request.getRemoteAddr()); 43 logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); 44 logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs())); 45 } 46 47 @AfterReturning(returning = "ret",pointcut = "webLog()") 48 public void doAfterReturning(Object ret) throws Throwable { 49 // 处理完请求,返回内容 50 logger.info("RESPONSE : " + ret); 51 } 52 }
执行
小结:
1) 在完成引入AOP依赖包之后,一般并不需要去做其他配置,在AOP的默认配置属性中,spring.aop.auto属性默认是开启的,也就是说引入AOP依赖后,默认已经增加了@EnableAspectJAutoProxy。
2) 定义切面类需要两个类的注解:@Component注解把切面类加入到IOC容器中;@Aspect注解使之成为切面类。
3) 切面类中:@Pointcut定义切入点;
@Before在连接点之前执行;
@After:当某连接点退出时执行的通知(不论是正常返回还是异常退出)。;
@AfterReturning记录返回的对象;@AfterThrowing:在方法抛出异常退出时执行的通知。
二、总结
切点标志符号:
AspectJ5的切点表达式由标志符和操作参数组成,如“execution(public * com.aop.aop.controller..*.*(..))“的切点表达式,execution就是标志符号,而圆括号里的“public * com.aop.aop.controller..*.*(..)”就是操作参数。
execution
匹配 join point 的执行, 例如 "execution(* hello(..))" 表示匹配所有目标类中的 hello() 方法. 这个是最基本的 pointcut 标志符.
within
匹配特定包下的所有 join point, 例如 within(com.xys.*)
表示 com.xys 包中的所有连接点, 即包中的所有类的所有方法. 而 within(com.xys.service.*Service)
表示在 com.xys.service 包中所有以 Service 结尾的类的所有的连接点.
this 与 target
this 的作用是匹配一个 bean, 这个 bean(Spring AOP proxy) 是一个给定类型的实例(instance of). 而 target 匹配的是一个目标对象(target object, 即需要织入 advice 的原始的类), 此对象是一个给定类型的实例(instance of).
bean
匹配 bean 名字为指定值的 bean 下的所有方法, 例如:
bean(*Service) // 匹配名字后缀为 Service 的 bean 下的所有方法
bean(myService) // 匹配名字为 myService 的 bean 下的所有方法
args
匹配参数满足要求的的方法.
例如:
@Pointcut("within(com.xys.demo2.*)")
public void pointcut2() {
}
@Before(value = "pointcut2() && args(name)")
public void doSomething(String name) {
logger.info("---page: {}---", name);
}
@Service
public class NormalService {
private Logger logger = LoggerFactory.getLogger(getClass());
public void someMethod() {
logger.info("---NormalService: someMethod invoked---");
}
public String test(String name) {
logger.info("---NormalService: test invoked---");
return "服务一切正常";
}
}
当 NormalService.test 执行时, 则 advice doSomething
就会执行, test 方法的参数 name 就会传递到 doSomething
中.
常用例子:
// 匹配只有一个参数 name 的方法
@Before(value = "aspectMethod() && args(name)")
public void doSomething(String name) {
}
// 匹配第一个参数为 name 的方法
@Before(value = "aspectMethod() && args(name, ..)")
public void doSomething(String name) {
}
// 匹配第二个参数为 name 的方法
Before(value = "aspectMethod() && args(*, name, ..)")
public void doSomething(String name) {
}
@annotation
匹配由指定注解所标注的方法, 例如:
@Pointcut("@annotation(com.xys.demo1.AuthChecker)")
public void pointcut() {
}
则匹配由注解 AuthChecker
所标注的方法.
常见的切点表达式
匹配方法签名
// 匹配指定包中的所有的方法
execution(* com.xys.service.*(..))
// 匹配当前包中的指定类的所有方法
execution(* UserService.*(..))
// 匹配指定包中的所有 public 方法
execution(public * com.xys.service.*(..))
// 匹配指定包中的所有 public 方法, 并且返回值是 int 类型的方法
execution(public int com.xys.service.*(..))
// 匹配指定包中的所有 public 方法, 并且第一个参数是 String, 返回值是 int 类型的方法
execution(public int com.xys.service.*(String name, ..))
匹配类型签名
// 匹配指定包中的所有的方法, 但不包括子包
within(com.xys.service.*)
// 匹配指定包中的所有的方法, 包括子包
within(com.xys.service..*)
// 匹配当前包中的指定类中的方法
within(UserService)
// 匹配一个接口的所有实现类中的实现的方法
within(UserDao+)
匹配 Bean 名字
// 匹配以指定名字结尾的 Bean 中的所有方法
bean(*Service)
切点表达式组合
// 匹配以 Service 或 ServiceImpl 结尾的 bean
bean(*Service || *ServiceImpl)
// 匹配名字以 Service 结尾, 并且在包 com.xys.service 中的 bean
bean(*Service) && within(com.xys.service.*)
”
三、来源
https://segmentfault.com/a/1190000007469968
https://www.cnblogs.com/lic309/p/4079194.html
https://blog.csdn.net/lmb55/article/details/82470388