Spring AOP底层源码解析之目标方法拦截与执行链
发布时间: 2024-02-17 08:52:12 阅读量: 36 订阅数: 39
# 1. 简介
### 1.1 什么是Spring AOP
Spring AOP (Aspect-Oriented Programming) 是 Spring 框架中用来实现面向切面编程的模块。在传统的面向对象编程中,我们将功能模块化封装成类,而面向切面编程是一种更加抽象和模块化的设计方法。它允许开发者定义横切关注点(cross-cutting concerns),如日志记录、性能统计、安全控制等,将这些关注点与核心业务逻辑分离开来,从而提高了代码的重用性和模块化程度。
Spring AOP 主要通过拦截器(Interceptor)和代理(Proxy)来实现,在不修改原始源码的基础上,通过“织入”(Weaving)的方式将横切逻辑应用到目标方法的执行过程中。
### 1.2 Spring AOP的应用场景
Spring AOP 可以用于如下场景:
- 日志记录:在方法执行前后记录日志信息
- 性能统计:统计方法的执行时间
- 安全控制:控制方法的访问权限
- 事务管理:管理方法的事务行为
- 异常处理:在方法执行过程中捕获并处理异常
- 缓存控制:管理方法的数据缓存
### 1.3 目标方法拦截与执行链的作用和原理
在 Spring AOP 中,目标方法拦截是指在方法执行前后对方法进行拦截,在拦截时可以加入自定义的逻辑。执行链则是将多个拦截器串联起来执行,从而形成拦截器链。目标方法拦截与执行链的作用是在目标方法执行的过程中进行干预和处理,比如记录日志、验证参数、处理异常等。
原理上,Spring AOP 使用代理模式,在目标对象的方法调用过程中,通过代理对象对目标方法进行拦截,在方法执行前后执行相应的拦截逻辑。Spring AOP 支持两种类型的代理:基于 JDK 动态代理和 CGLIB 动态代理。代理对象的生成和方法的代理调用是通过 AOP 框架在运行时动态完成的。
# 2. Spring AOP的核心组件
Spring AOP的核心组件包括切面(Aspect)、连接点(Join Point)、切点(Pointcut)、通知(Advice)和织入(Weaving)。下面,我们将逐一介绍这些组件的作用和原理。
### 2.1 切面(Aspect)
切面是指横切关注点的模块化单元,它是Spring AOP的基本元素。切面由切点和通知组成,它定义了在哪些连接点上应用通知。
### 2.2 连接点(Join Point)
连接点是在应用程序执行过程中能够插入切面的点。Spring AOP通过代理来实现切面的插入,所以连接点实际上是被代理对象的方法。
### 2.3 切点(Pointcut)
切点用来定义一组连接点的集合,通常使用表达式来描述。切点是切面中最重要的部分,它决定了通知应该在哪些连接点上执行。
### 2.4 通知(Advice)
通知定义了在切面的何处应用通知,通知是在切点上执行的代码,它定义了在方法执行之前、之后或周围执行的行为。
Spring AOP提供了五种类型的通知:
- 前置通知(Before):在方法执行之前执行。
- 后置通知(After):在方法执行之后执行,无论是否发生异常。
- 返回通知(After-returning):在方法正常返回后执行。
- 异常通知(After-throwing):在方法抛出异常后执行。
- 环绕通知(Around):在方法执行之前和之后执行自定义的行为。
### 2.5 织入(Weaving)
织入是将切面应用到目标对象上并创建代理对象的过程。Spring AOP支持两种织入方式:编译时织入和运行时织入。编译时织入需要使用特定的编译器来进行织入,而运行时织入是在运行时通过动态代理来完成的。
织入可以在目标对象的类加载时进行(静态代理),也可以在运行时通过动态代理代理对象来完成。Spring AOP通过动态生成代理对象,并将通知织入到目标对象的方法中,从而实现了切面的功能。
总结:
- 切面是由切点和通知组成的,用于定义横切关注点的模块化单元。
- 连接点是可以插入切面的程序执行点,通常是被代理对象的方法。
- 切点用来定义一组连接点的集合,通过表达式描述。
- 通知指定了切面在切点上要执行的行为,包括前置、后置、返回、异常和环绕通知。
- 织入是将切面应用到目标对象上并创建代理对象的过程,可以在编译时或运行时进行。
# 3. 目标方法拦截与执行链的实现原理
在本章节中,我们将深入探讨Spring AOP中目标方法拦截与执行链的实现原理,包括JDK动态代理与CGLIB动态代理、Spring框架中的AOP代理以及目标方法拦截与执行链的执行顺序。通过对实现原理的理解,我们可以更好地应用和扩展Spring AOP的功能。
#### 3.1 JDK动态代理与CGLIB动态代理
Spring AOP的实现依赖于动态代理,其中JDK动态代理和CGLIB动态代理是两种常见的实现方式。在目标方法拦截与执行链中,Spring根据不同的情况选择合适的动态代理方式进行代理和拦截。
##### JDK动态代理
JDK动态代理是基于接口的代理,它通过反射机制动态生成代理类和代理实例。当目标对象实现了接口时,Spring AOP会使用JDK动态代理来实现AOP。JDK动态代理要求目标对象必须实现接口,因此它的局限性在于无法代理未实现接口的类。
下面是使用JDK动态代理的示例代码:
```java
// 定义接口
public interface UserService {
void addUser(String username);
}
// 目标对象
public class UserServiceImpl implements UserService {
@Override
public void addUser(String username) {
System.out.println("Adding user: " + username);
}
}
// 创建切面类
public class LogAspect {
public void before() {
System.out.println("Before method execution");
}
}
// 使用JDK动态代理
public class Main {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
LogAspect logAspect = new LogAspect();
ProxyFactory proxyFactory = new ProxyFactory(userService, logAspect);
UserService proxy = (UserService) proxyFactory.getProxy();
proxy.addUser("Alice");
}
}
```
在上述代码中,我们对UserService接口实现了目标对象UserServiceImpl,并使用JDK动态代理创建了代理对象,同时关联了日志切面LogAspect。
##### CGLIB动态代理
CGLIB动态代理是基于继承的代理,在运行时动态生成一个指定类的子类,并重写其中的方法来实现代理。当目标对象未实现接口时,Spring AOP会使用CGLIB动态代理来实现AOP。CGLIB动态代理不要求目标对象实现接口,因此它具有更广泛的适用性。
下面是使用CGLIB动态代理的示例代码:
```java
// 目标对象
public class ProductService {
public void addProduct(String productName) {
System.out.println("Adding product: " + productName);
}
}
// 创建切面类
public class LogAspect {
public void before() {
System.out.println("Before method execution");
}
}
// 使用CGLIB动态代理
public class Main {
public static void main(String[] args) {
Enh
```
0
0