基础
Spring 系列:Spring AOP 中@Pointcut的用法(多个Pointcut)
Spring AOP使用JDK动态代理或者CGLIB来为目标对象创建代理。(建议优先使用JDK的动态代理)
- 如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理。所有该目标类型实现的接口都将被代理。 若该目标对象没有实现任何接口,则创建一个CGLIB代理。
- 如果你希望强制使用CGLIB代理,(例如:希望代理目标对象的所有方法,而不只是实现自接口的方法) 那也可以。但是需要考虑以下问题:
- 无法通知(advise)final方法,因为他们不能被覆写。
- 代理对象的构造器会被调用两次。因为在CGLIB代理模式下每一个代理对象都会 产生一个子类。每一个代理实例会生成两个对象:实际代理对象和它的一个实现了通知的子类实例 而是用JDK代理时不会出现这样的行为。通常情况下,调用代理类型的构造器两次并不是问题, 因为除了会发生指派外没有任何真正的逻辑被实现。
- CGLib的效率没有使用JDK代理机制高,速度平均要慢8倍左右。
Demo
AopLogApplication
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AopLogApplication {
public static void main(String[] args) {
SpringApplication.run(AopLogApplication.class, args);
}
}
AopLogController
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AopLogController {
@GetMapping("/a1")
public String aoptest1(){
return " aop test1";
}
@GetMapping(value = "/a2")
public String aoptest2(){
return " aop test2";
}
}
AopLog
package com.example.demo.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
@Aspect
@Component
public class AopLog {
private Logger logger = LoggerFactory.getLogger(this.getClass());
ThreadLocal<Long> startTime = new ThreadLocal<>();
/**
* 定义切入点:在哪些类、哪些方法切入
* 拦截 com.example所有类的,所有方法
* AOP只能匹配public方
*/
@Pointcut("execution( * com.example..*.*( .. ) )")
public void aopWebLogAll() {
System.out.printf("--enter---");
}
/**
* 定义切入点:在哪些类、哪些方法切入
* 拦截 com.example下AopLogController类的*est1结尾的所有public方法
*/
@Pointcut("execution( public * com.example..AopLogController.*est1( .. ) )")
private void aopWebLog() {
System.out.printf("--enter---");
}
@Before("aopWebLogAll()")
public void doBefore2(JoinPoint joinPoint) throws Throwable {
startTime.set(System.currentTimeMillis());
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 记录下请求内容
System.out.println("========doBefore All==============");
logger.info("URL : " + request.getRequestURL().toString());
logger.info("HTTP方法 : " + request.getMethod());
logger.info("IP : " + request.getRemoteAddr());
logger.info("类的方法 : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
logger.info("参数 : " + Arrays.toString(joinPoint.getArgs()));
logger.info("参数 : " + request.getQueryString());
}
@AfterReturning(pointcut = "aopWebLogAll()",returning = "retObject")
public void doAfterReturning2(Object retObject) throws Throwable {
System.out.println("========doAfterReturning All=============");
// 处理完请求,返回内容
logger.info("应答值 : " + retObject);
logger.info("用时: " + (System.currentTimeMillis() - startTime.get()));
}
//抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行的通知。
@AfterThrowing(pointcut = "aopWebLogAll()", throwing = "ex")
public void addAfterThrowingLogger2(JoinPoint joinPoint, Exception ex) {
System.out.println("========addAfterThrowingLogger All==============");
logger.error("执行 " + " 异常", ex);
}
@Before("aopWebLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
startTime.set(System.currentTimeMillis());
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 记录下请求内容
System.out.println("========doBefore==============");
logger.info("URL : " + request.getRequestURL().toString());
logger.info("HTTP方法 : " + request.getMethod());
logger.info("IP : " + request.getRemoteAddr());
logger.info("类的方法 : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
logger.info("参数 : " + Arrays.toString(joinPoint.getArgs()));
logger.info("参数 : " + request.getQueryString());
}
@AfterReturning(pointcut = "aopWebLog()",returning = "retObject")
public void doAfterReturning(Object retObject) throws Throwable {
System.out.println("========doAfterReturning==============");
// 处理完请求,返回内容
logger.info("应答值 : " + retObject);
logger.info("用时: " + (System.currentTimeMillis() - startTime.get()));
}
//抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行的通知。
@AfterThrowing(pointcut = "aopWebLog()", throwing = "ex")
public void addAfterThrowingLogger(JoinPoint joinPoint, Exception ex) {
System.out.println("========addAfterThrowingLogger==============");
logger.error("执行 " + " 异常", ex);
}
}
pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>AopLog</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
验证
========doBefore==============
2020-07-18 15:53:02.110 INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog : URL : http://localhost:8080/a1
2020-07-18 15:53:02.110 INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog : HTTP方法 : GET
2020-07-18 15:53:02.110 INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog : IP : 0:0:0:0:0:0:0:1
2020-07-18 15:53:02.110 INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog : 类的方法 : com.example.demo.controller.AopLogController.aoptest1
2020-07-18 15:53:02.110 INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog : 参数 : []
2020-07-18 15:53:02.110 INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog : 参数 : null
========doBefore All==============
2020-07-18 15:53:02.111 INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog : URL : http://localhost:8080/a1
2020-07-18 15:53:02.111 INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog : HTTP方法 : GET
2020-07-18 15:53:02.111 INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog : IP : 0:0:0:0:0:0:0:1
2020-07-18 15:53:02.111 INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog : 类的方法 : com.example.demo.controller.AopLogController.aoptest1
2020-07-18 15:53:02.111 INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog : 参数 : []
2020-07-18 15:53:02.111 INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog : 参数 : null
========doAfterReturning==============
2020-07-18 15:53:02.111 INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog : 应答值 : aop test1
2020-07-18 15:53:02.111 INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog : 用时: 1
========doAfterReturning All=============
2020-07-18 15:53:02.111 INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog : 应答值 : aop test1
2020-07-18 15:53:02.111 INFO 13588 --- [nio-8080-exec-9] com.example.demo.aop.AopLog : 用时: 1
========doBefore All==============
2020-07-18 15:53:24.462 INFO 13588 --- [nio-8080-exec-1] com.example.demo.aop.AopLog : URL : http://localhost:8080/a2
2020-07-18 15:53:24.462 INFO 13588 --- [nio-8080-exec-1] com.example.demo.aop.AopLog : HTTP方法 : GET
2020-07-18 15:53:24.462 INFO 13588 --- [nio-8080-exec-1] com.example.demo.aop.AopLog : IP : 0:0:0:0:0:0:0:1
2020-07-18 15:53:24.462 INFO 13588 --- [nio-8080-exec-1] com.example.demo.aop.AopLog : 类的方法 : com.example.demo.controller.AopLogController.aoptest2
2020-07-18 15:53:24.462 INFO 13588 --- [nio-8080-exec-1] com.example.demo.aop.AopLog : 参数 : []
2020-07-18 15:53:24.462 INFO 13588 --- [nio-8080-exec-1] com.example.demo.aop.AopLog : 参数 : null
========doAfterReturning All=============
2020-07-18 15:53:24.463 INFO 13588 --- [nio-8080-exec-1] com.example.demo.aop.AopLog : 应答值 : aop test2
2020-07-18 15:53:24.463 INFO 13588 --- [nio-8080-exec-1] com.example.demo.aop.AopLog : 用时: 1