文章目录
Spring AOP
一、AOP 概述
1、概述
AOP 全称为 Aspect Oriented Programming,即 面向切面编程,是一种编程范式。
AOP 用于将横切关注点(cross-cutting concerns)从核心业务逻辑中分离出来,以提高代码的模块化、可维护性和重用性。横切关注点是指那些在应用程序中存在于多个模块和层次中的功能,如日志记录、事务管理、安全性、缓存、性能监控等。
Spring 框架提供了强大的 AOP 支持,通过 Spring AOP,可以很方便地实现横切关注点的模块化。
2、原理
Spring AOP 采用了代理模式实现,通过动态代理或者字节码生成技术来在运行时为目标对象创建代理对象,并在代理对象的方法调用前后执行通知。实现在【不修改java源代码】的情况下,【运行时】实现方法功能的【增强】
代理模式:
-
静态代理:程序编译运行前由程序员或工具手动创建。
-
动态代理:在运行时动态生成代理类,用于代理目标对象的方法调用。
JDK 动态代理和 CGLIB 动态代理是两种常见的 Java 动态代理实现方式:
- Jdk动态代理:
- Java 标准库提供的一种动态代理实现方式,基于 Java 反射机制。
- 要求目标对象必须实现一个或多个接口,代理对象和目标对象实现了相同的接口。
- Cglib动态代理:
- 在运行时动态生成目标对象的子类作为代理类,而不需要目标对象实现接口。
Jdk动态代理适用于对接口
进行代理,Cglib动态代理适用于对类
进行代理。
3、相关术语
AOP 的核心思想是通过切面(Aspect)将横切关注点模块化,并将其应用到程序的不同部分。
切面(Aspect)由切点(Pointcut)和通知(Advice)组成,
- 其中切点定义了在何处应用通知,而通知定义了在切点处执行的操作。
# Joinpoint(连接点):
待增强功能的方法。(动态,一个动作)
# Pointcut(切入点):
要拦截的Joinpoint。(静态,一个点)
# Advice(通知):
拦截到Joinpoint之后要做的事情,即增强的功能
通知类型:前置通知、后置通知、异常通知、最终通知、环绕通知
# Target(目标对象):
被代理的对象。
# Proxy(代理对象):
代理类对象(一个类被AOP织入增强后产生)
# Weaving(织入):
增强目标对象,创建代理对象的过程
# Aspect(切面):
切入点 + 通知
4、五种通知类型
# 通知类型
1. 前置通知 @Before
在目标方法 执行前 执行
2. 后置通知 @AfterReturning
在目标方法 正常返回后 执行。(它和异常通知只能执行一个)
3. 异常通知 @AfterThrowing
在目标方法 发生异常后 执行。(它和后置通知只能执行一个)
4. 最终通知 @After
无论目标方法正常返回,还是发生异常都会执行
5. 环绕通知 @Around
在目标方法 执行前后 执行该增强方法。(一定要抛出异常,才能回滚事务)
# 执行顺序
1. 正常
@Before -> @Around -> Method -> @Around -> @After -> @AfterReturning
2. 异常
@Before -> @Around -> Method -> @Around -> @After -> @AfterThrowing
其中需要注意:使用@Around
时,一定要将异常抛出,否则事务不会回滚。
二、手写简易AOP
步骤分析:
- 创建两个代理类,增强被代理对象的功能
- 创建代理类工厂,获取代理类对象
- 初始化IOC时,使用动态代理加入切面AOP
0、被代理类/接口
public class AopClass {
public UserEntity test() {
return UserEntity.builder().id(1L).username("测试name").build();
}
}
public interface AopInterface {
UserEntity test();
}
public class AopInterfaceImpl implements AopInterface {
@Override
public UserEntity test() {
return UserEntity.builder().id(1L).username("测试name").build();
}
}
1、Jdk代理类
/**
* Jdk动态代理
*/
public class JdkProxy implements InvocationHandler {
/**
* 被代理的对象(Target 目标对象)
*/
private Object targetObject;
/**
* 需要被代理的方法(JoinPoint 连接点)
*/
public String joinPoint = "test";
/**
* 初始化 被代理的对象
*/
public JdkProxy(Object targetObject) {
this.targetObject = targetObject;
}
/**
* Object proxy:代理类对象
* Method method:执行的目标方法
* Object[] args:方法参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 执行的目标方法名称
String name = method.getName();
// 判断是否需要代理(PointCut 切入点)
if (joinPoint.equals(name)) {
// 前置增强(Advice 通知)
System.out.println("执行了JDK代理");
}
// 具体执行目标方法
return method.invoke(targetObject, args);
}
}
2、Cglib代理类
/**
* Cglib动态代理
*/
public class CglibProxy implements MethodInterceptor {
/**
* 被代理的对象(Target 目标对象)
*/
private Object targetObject;
/**
* 要代理的目标方法(JoinPoint 连接点)
*/
public String joinPoint = "test";
/**
* 初始化 被代理的对象
*/
public CglibProxy(Object targetObject) {
this.targetObject = targetObject;
}
/**
* Object o:代理类对象
* Method method:执行的目标方法
* Object[] objects:方法参数
* MethodProxy methodProxy:方法的代理
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// 执行的目标方法名称
String name = method.getName();
// 判断是否需要代理(PointCut 切入点)
if (joinPoint.equals(name)){
// 前置增强(Advice 通知)
System.out.println("执行了Cglib代理");
}
// 具体执行目标方法
// return methodProxy.invokeSupe