AOP
AOP即面向切面编程,在一个web服务中,比如说我们需要每次记录请求的处理时间,或者要记录日志等等功能的时候,如果要把这些写在代码中的话,就很麻烦,产生很多相同的代码,这时候就可以使用AOP, 通过对现有的程序定义一个切入点,然后在其前后切入不同的执行内容,下面来看一个简单的例子,就比如要记录每个请求的处理耗时.
AOP示例
首先需要引入AOP的依赖,如下:1
2
3
4
5<!-- AOP -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
然后我们来创建一个类 TimerAspect
,然后加入 @Aspect
注解 表示这是一个切面类,还有一个是 @Component
交给Spring容器去管理
定义好类之后,还需要一个具体处理逻辑的方法,代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24@Aspect
@Component
@Slf4j
public class TimerAspect {
@Around("execution(* com.security.example.demo.controller.TestController.*(..))")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
// 拿到请求的所有参数
Object[] params = pjp.getArgs();
// 记录开始时间
long startTime = System.currentTimeMillis();
// 执行controller中具体的业务逻辑
Object object = pjp.proceed();
// 方法执行完成打印结束时间
log.info("方法执行结束,耗时:{}ms", System.currentTimeMillis() - startTime);
// 返回的是具体controller的返回值
return object;
}
}
这里首先来看一下方法上面这个注解,一共有以下几种:
@Before
: 在切入点开始处执行@After
: 在切入点的结尾处执行@AfterReturning
: 在切入点方法return之后执行(可以对返回值做一些处理)@Around
: 可以在切入点前后都执行内容,可以自己控制何时执行切入点自身的内容@AfterThrowing
: 用来处理当切入内容部分抛出异常之后的处理逻辑
然后再来看一下我们上面用的是@Around
,这个也是最常用的,方法里面可以通过 ProceedingJoinPoint
对象,拿到方法的参数等,然后可以通过pjp.proceed();
执行切入点自身的内容,并且拿到返回值,方法最后再把返回值返回去就ok了
这里还有一个是注解后面定义的切入点,在上面的例子中execution(* com.security.example.demo.controller.TestController.*(..))
,就表示 com.security.example.demo.controller.TestController
这个类里面的所有方法 (..)
表示任意的参数,可以根据自己的实际业务调整
测试
我们现在有这样一个接口:1
2
3
4
5@GetMapping("{id:\\d+}")
public User getInfo(@PathVariable Long id){
log.info("查询id是[{}]的数据.....", id);
return new User();
}
然后启动请求接口,看一下控制台的输出日志:
AOP中的同步问题
我们上面用的是@Around
实现了打印请求耗时,当然也可以通过使用@doBefore
和 @doAfterReturning
这两个方法来实现, 这样的话就需要在类里面定义一个成员变量,这里就会有线程同步的问题, 可以通过使用 ThreadLocal
解决,但是会增加内存开销,所以还是直接用 @Around
方便
AOP切面的优先级
假设我们有多个切面,比如一个切面先做参数处理, 另一个做返回处理, 这里就需要规定每个切面的执行顺序了, 可以通过 @Order(i)
这个注解来设置切面的优先级, i的值越小,优先级越高
举个例子, 一个是处理参数的切面 设置@Order(2)
, 一个是记录耗时的切面,设置@Order(1)
, 这里记录耗时的切面优先级就高,这时候的执行顺序是这样的:
- 在
@Before
中优先执行@Order(1)
的内容, 再执行@Order(2)
的内容 - 在
@After
和@AfterReturning
中优先执行@Order(2)
的内容,再执行@Order(1)
的内容
总结一下就是:
- 在切入点前的操作,按order的值由小到大执行
- 在切入点后的操作,按order的值由大到小执行