目录
- 什么是 Spring AOP ?
- AOP 是啥 ?
- Spring AOP 可以干啥 ?
- AOP 的组成
- Spring AOP 的实现
- Spring AOP 的实现原理
什么是 Spring AOP ?
AOP 是啥 ?
我们知道 OOP 是面向对象编程, 那 AOP 又是啥呢 ?
AOP(Aspect Oriented Programming):⾯向切⾯编程,它是⼀种思想,它是对某⼀类事情的集中处理。
什么又是面向切面编程?
就比如:我上 CSDN 发布文章时,它会先查询我的登录状态,我查询个人文章列表时,也要查询登录状态,查看个人主页也一样,这些功能的共同点是查询登录状态,那判断用户是否登录这件事,它就是一个切面,他就是某一类事,面向切面编程就是面向某一类事进行编程,将某一类事集中处理了。(这样就可以把精力放在业务逻辑上,而不是编写重复代码)
AOP 是⼀种思想,⽽ Spring AOP 是⼀个框架,提供了⼀种对 AOP 思想的实现,它们的关系和 IoC 与 DI 类似。
Spring AOP 可以干啥 ?
统一用户登录判断
统一日志记录
统一方法执⾏时间统计
统⼀的返回格式设置
统⼀的异常处理
事务的开启和提交等等
AOP 是针对某一类通用的事件进行统一处理,它是 OOP 的一种补充。
AOP 的组成
-
切面 (Aspect):定义的是什么事件(AOP 是干什么的,比如:是用户登录校验)
-
切点 (Pointcut):定义具体规则(定义用户拦截规则,比如:哪些接口需要判断用户登录,哪些接口不需要判断)
-
通知 (Advice):AOP 执行的具体方法(比如:获取用户登录信息,获取到则说明已登录)
通知又分为以下几种:
前置通知 @Before:通知⽅法会在⽬标⽅法调⽤之前执⾏。
后置通知 @After:通知⽅法会在⽬标⽅法返回或者抛出异常后调⽤。
返回通知 @AfterReturning:通知⽅法会在⽬标⽅法返回后调⽤。
异常通知 @AfterThrowing:通知⽅法会在⽬标⽅法抛出异常后调⽤。
环绕通知 @Around:通知包裹了被通知的⽅法,在被通知的⽅法通知之前和调⽤之后执行自定义的行为。
- 连接点 (Join Point):所有可能触发切点的点(比如:所有接口)
Spring AOP 的实现
- 添加 Spring AOP 依赖:
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-bo
ot-starter-aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 定义切面:
@Aspect //表明此类为一个切面
@Component // 随着spring框架启动而启动
public class UserAspect {
}
- 定义切点:
@Aspect //表明此类为一个切面
@Component // 随着spring框架启动而启动
public class UserAspect {
// 切点,这⾥使⽤ AspectJ 表达式语法
//拦截 UserController 里所有方法
@Pointcut("execution(* com.example.demo.controller.UserController.* (..))")
public void pointcut(){
}
}
- 执行通知:
先将被拦截的类实现:
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/getuser")
public String getUser() {
System.out.println("do getUser");
return "get User";
}
@RequestMapping("/deluser")
public String delUser() {
System.out.println("do delUser");
return "del User";
}
}
@Aspect
@Component
public class UserAspect {
@Pointcut("execution(* com.example.demo.controller.UserController.* (..))")
public void pointcut(){
}
//该前置通知针对于这个切点
@Before("pointcut()")
public void doBefore() {
System.out.println("执行了前置通知");
}
}
确实是先执行前置通知,再执行被拦截的方法。
再来执行个后置通知:
@After("pointcut()")
public void doAfter() {
System.out.println("执行了后置通知");
}
再次访问:
环绕通知:
// 添加环绕通知
@Around("pointcut()")
public Object doAround(ProceedingJoinPoint joinPoint){
Object obj = null;
System.out.println("Around ⽅法开始执⾏");
try {
// 执⾏目标⽅法
obj = joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("Around ⽅法结束执⾏");
return obj;
}
环绕通知是在最前面和最后面执行的。(在多线程中可以统计目标方法的执行时间,单线程下也可以使用前置通知和后置通知来统计)
Spring AOP 的实现原理
Spring AOP 是构建在动态代理基础上,因此 Spring 对 AOP 的⽀持局限于⽅法级别的拦截。
Spring 动态代理组成:
- JDK Proxy(代理对象必须实现接口,才能使用 JDK Proxy)
- CGLIB(通过实现代理类的子类来实现动态代理,被 final 修饰的类不能被代理)
JDK Proxy 和 CGLIB 区别:
- 实现不同:DK Proxy 要求代理类实现接口才能被代理,CGLIB 通过实现代理类的子类来实现动态代理
- 性能不同:JDK 7+ JDK Proxy 的性能高于 CGLIB,JDK 7 前则低于