一:什么是Spring AOP?
首先,AOP是一种思想,它是对某一类事情的集中处理。 如用户登录权限的效验,没学 AOP 之前,我们所有需要判断用户登录的页面,都要各自实现或调用验证的方法。然后有了 AOP 之后,我们只需要在某一处配置一下,所有需要,判断用户登录的页面就全部可以实现用户登录验证了,不再需要在每个方法中都写相同的用户登录验证了。
AOP是一种思想,而Spring AOP是这种思想的一种具体实现。
二:AOP的作用
三:如何学习Spring AOP?
Spring AOP 学习主要分为以下 3 个部分:
- 学习 AOP 是如何组成的?也就是学习 AOP 组成的相关概念。
- 学习 Spring AOP 使⽤。
- 学习 Spring AOP 实现原理。
四:AOP的组成
AOP整个组成部分的概念示意图,以多个页面都要访问用户登录权限为例:
五:Spring AOP的使用
概述:
a).添加Spring AOP 框架支持;
b).定义切面和切点;
c).定义通知。
1.添加Spring AOP 框架支持。
在 pom.xml 中添加如下配置:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.定义切面和切点。
package com.example.demo.aop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* 登录的AOP实现代码
*/
@Component
@Aspect // 标识当前类为一个切面
public class LoginAOP {
// 定义切点
@Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
public void pointcut() {
}
}
其中 pointcut ⽅法为空⽅法,它不需要有⽅法体,此⽅法名就是起到⼀个“标识”的作⽤,标识下⾯的
通知⽅法具体指的是哪个切点(因为切点可能有很多个)。
3.定义通知。
package com.example.demo.aop;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* 登录的AOP实现代码
*/
@Component
@Aspect // 标识当前类为一个切面
public class LoginAOP {
// 定义切点
@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("执行了后置通知");
}
// return 之前通知
@AfterReturning("pointcut()")
public void doAfterReturning(){
System.out.println("执行了返回之前通知");
}
// 抛出异常之前通知
@AfterThrowing("pointcut()")
public void doAfterThrowing(){
System.out.println("执行了抛出异常之前通知");
}
}
测试:
查看打印信息:
以上就是最基础的Spring AOP的实现。
与以上四种通知类型不同的是环绕通知。
package com.example.demo.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* 登录的AOP实现代码
*/
@Component
@Aspect // 标识当前类为一个切面
public class LoginAOP {
// 定义切点
@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("执行了后置通知");
}
// return 之前通知
@AfterReturning("pointcut()")
public void doAfterReturning(){
System.out.println("执行了返回之前通知");
}
// 抛出异常之前通知
@AfterThrowing("pointcut()")
public void doAfterThrowing(){
System.out.println("执行了抛出异常之前通知");
}
// 环绕通知
@Around("pointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) {
Object obj = null;
System.out.println("环绕通知开始执行");
try {
obj = joinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("环绕通知结束执行");
return obj;
}
}
我们发现,环绕通知是最先开始最后结束的。
解释:
六:Spring AOP实现原理
Spring AOP 是构建在动态代理基础上,因此 Spring 对 AOP 的⽀持局限于⽅法级别的拦截。
Spring AOP ⽀持 JDK Proxy 和 CGLIB ⽅式实现动态代理。默认情况下,实现了接⼝的类,使⽤ AOP 会基于 JDK ⽣成代理类,没有实现接⼝的类,会基于 CGLIB ⽣成代理类。
织入(Weaving):代理的生成时机。
织⼊是把切⾯应⽤到⽬标对象并创建新的代理对象的过程,切⾯在指定的连接点被织⼊到⽬标对象中。
动态代理:
此种实现在设计模式上称为动态代理模式,在实现的技术⼿段上,都是在 class 代码运行期,动态的织⼊字节码。
Spring 框架中的AOP,主要基于两种方式:JDK 及 CGLIB 的方式。这两种方式的代理⽬标都是被代理类中的⽅法,在运⾏期,动态的织⼊字节码⽣成代理类。
Q : JDK和CGLIB实现的区别?
A:
- JDK 实现,要求被代理类必须实现接⼝,之后是通过 InvocationHandler 及 Proxy,在运⾏时动态的在内存中⽣成了代理类对象,该代理对象是通过实现同样的接⼝实现(类似静态代理接⼝实现的⽅式),只是该代理类是在运⾏期时,动态的织⼊统⼀的业务逻辑字节码来完成。
- CGLIB 实现,被代理类可以不实现接⼝,是通过继承被代理类,在运⾏时动态的⽣成代理类对象。
七:总结
关于Spring AOP,我们了解了:
AOP 是对某方面能力的统⼀实现,它是⼀种实现思想,Spring AOP 是对 AOP 的具体实现,Spring AOP 可通过 AspectJ(注解)的⽅式来实现 AOP 的功能,Spring AOP 的实现步骤是:
- 添加 AOP 框架⽀持。
- 定义切面和切点。
- 定义通知。
Spring AOP 是通过动态代理的方式,在运行期间将 AOP 代码织⼊到程序中的,它的实现⽅式有两种:JDK Proxy 和 CGLIB。