Spring AOP为我们提供了切面通知(Advice)的功能,我们可以使用切面通知在函数的前后某些位置插入一些我们希望进行的操作,例如记录日志,检查合法性等等。
使用切面通知可以通过配置文件和注解两种方式,本文仅讨论注解方式,因为配置文件方式太麻烦了,懒得弄。。。
需要引入的依赖包:
使用切面通知需要引入相关的jar包:spring-aop.jar
如果要使用注解的方式那么还需要引入:aspectjweaver.jar和aopalliance.jar
一、一个简单的Advice
一个简单的切面通知实例
package com.baymax.study.study_springaop.advice;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class StudyAdvice {
//在被通知函数执行前执行
@Before(value = "execution(void com.baymax.study.study_springaop.controller.*Controller.*Method*())")
public void before(JoinPoint point) {
System.out.println("before");
}
//在被通知函数返回后执行
@AfterReturning("execution(void com.baymax.study.study_springaop.controller.*Controller.*Method*())")
public void afterReturning(JoinPoint point) {
System.out.println("after returning");
}
//在被通知函数执行完毕返回之前执行
@After(value = "execution(void com.baymax.study.study_springaop.controller.*Controller.*Method*())")
public void after(JoinPoint point) {
System.out.println("after");
}
//环绕通知,在被通知函数执行前、后执行
@Around(value = "execution(void com.baymax.study.study_springaop.controller.*Controller.*Method*())")
public void around(ProceedingJoinPoint point) {
try {
System.out.println("around 1");
point.proceed();//执行被通知函数
System.out.println("around 2");
} catch (Throwable e) {
e.printStackTrace();
}
}
}
解析:
- 使用@Component注解将该切面通知实例托管在Spring IoC容器中;
- 使用@Aspect注解声明这是一个切面通知的实例;
- 通过
execution(void com.baymax.study.study_springaop.controller.Controller.Method*())
定位到相应的被通知函数。
需要被通知的函数:
package com.baymax.study.study_springaop.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/study")
public class StudyController {
@RequestMapping("/method1")
public void studyMethod1() {
System.out.println("in method 1");
}
}
注意:因为切面通知的功能实际上是基于Spring IoC容器扩展而来的功能,所以被通知的函数的实例必须是托管在Spring IoC容器中的,当然切面通知实例本身也需要被托管在Spring IoC容器中。
上述代码执行结果:
around 1
before
in method 1
around 2
after
after returning
结论:可以看到@Before注解标注的切面通知在被通知函数执行前执行,@After注解标注的切面通知在被通知函数执行完毕返回之前执行,@AfterReturning注解标注的切面通知在被通知函数返回后执行,@Around注解标注的切面通知可以在被通知函数执行前后分别执行一系列逻辑,从执行结果可以看到,around 1先于before执行,around 2先于after执行。
二、获取被通知的方法的属性
可以通过point.getSignature()获取到被通知函数的反射实例,然后进行一系列操作。
例如获取被通知函数的注解:
ActionDescription annotation = ((MethodSignature) point.getSignature()).getMethod().getAnnotation(ActionDescription.class);
其他的一些操作和反射基本相同,需要用到时再自行探索。
三、在切面中如何获取到当前的Spring MVC的request实例
可以使用如下方法获取到当前的request:
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();