Spring AOP(面向切面编程) 是Spring框架中用于解耦横切关注点(如日志、事务、安全等)的核心技术。它通过动态代理机制,在不修改业务代码的前提下,将通用逻辑“织入”到目标方法中。本文从源码层面解析Spring AOP的实现原理,并通过代码示例帮助读者彻底掌握它的核心概念和应用场景!
一、什么是Spring AOP?
1. 核心概念
- AOP(Aspect-Oriented Programming) :面向切面编程,是一种编程范式。
- 横切关注点:多个模块中重复的逻辑(如日志、事务、权限校验)。
- 核心思想:将横切逻辑与业务逻辑分离,提高代码复用性和可维护性。
2. 为什么需要AOP?
假设一个业务系统中需要为所有Service方法添加日志和事务管理:
- 传统方式:在每个方法中手动添加日志和事务代码,导致代码冗余。
- AOP方式:通过切面统一处理,业务代码无需任何修改!
二、Spring AOP的核心组件
1. 切面(Aspect)
定义横切逻辑的模块。例如,一个日志切面包含记录方法执行时间的逻辑。
2. 连接点(Join Point)
程序执行过程中可插入切面的点。例如,方法调用、异常抛出等。
3. 通知(Advice)
切面在连接点执行的具体逻辑,分为五种类型:
- @Before:方法执行前执行。
- @After:方法执行后执行(无论是否抛出异常)。
- @AfterReturning:方法正常返回后执行。
- @AfterThrowing:方法抛出异常后执行。
- @Around:包裹目标方法,可控制方法执行。
4. 切点(Pointcut)
通过表达式定义哪些连接点需要应用通知。例如,匹配所有Service层的方法。
5. 示例代码
@Aspect
@Component
public class LogAspect {
// 定义切点:匹配所有Service层的方法
@Pointcut("execution(* com.example.service.*.*(..))")
public void servicePointcut() {}
// 定义通知:方法执行前打印日志
@Before("servicePointcut()")
public void logBefore(JoinPoint joinPoint) {
System.out.println("调用方法: " + joinPoint.getSignature().getName());
}
}
三、Spring AOP的实现方式
1. 基于代理的AOP(Spring原生实现)
实现原理:
- JDK动态代理:针对实现了接口的类,通过
Proxy
类生成代理对象。 - CGLIB代理:针对未实现接口的类,通过生成子类继承目标类。
源码解析:
Spring通过ProxyFactory
类选择代理方式:
public class ProxyFactory {
public Object getProxy() {
if (isInterfaceProxyingEnabled()) {
return createJdkDynamicProxy(); // JDK动态代理
} else {
return createCglibProxy(); // CGLIB代理
}
}
}
示例场景:
- 目标类实现了接口 → 使用JDK动态代理。
- 目标类未实现接口 → 使用CGLIB代理。
2. 基于AspectJ的AOP
实现原理:
- 编译时织入(CTW) :在编译阶段修改字节码,直接生成包含切面逻辑的类。
- 加载时织入(LTW) :在类加载时通过类加载器修改字节码。
与Spring AOP的区别:
- Spring AOP:运行时动态代理,仅支持方法级别的切面。
- AspectJ:更强大,支持字段、构造器级别的切面,但需要额外配置。
四、Spring AOP的源码级执行流程
以@Around
通知为例,解析代理方法的执行过程:
- 生成代理对象:通过
ProxyFactory
创建代理类。 - 拦截方法调用:代理对象调用方法时,触发
MethodInterceptor
。 - 执行通知链:按顺序执行所有通知(Before → Around → After等)。
- 反射调用目标方法:最终通过反射调用原始方法。
关键源码片段:
public class JdkDynamicAopProxy implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) {
// 获取拦截器链(即所有通知)
List<MethodInterceptor> interceptors = getInterceptors(method);
// 创建方法调用链
MethodInvocation invocation = new ReflectiveMethodInvocation(...);
// 执行拦截器链
return invocation.proceed();
}
}
五、Spring AOP的常见应用场景
1. 日志记录
统一记录方法的入参、返回值、执行时间。
@Around("servicePointcut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
System.out.println("方法执行耗时: " + (end - start) + "ms");
return result;
}
2. 事务管理
通过@Transactional
注解实现声明式事务。
@Transactional
public void transferMoney(Account from, Account to, double amount) {
// 业务逻辑
}
3. 权限校验
在方法执行前验证用户权限。
@Before("servicePointcut() && args(user)")
public void checkPermission(User user) {
if (!user.hasPermission()) {
throw new SecurityException("无权限操作!");
}
}
六、Spring AOP的局限性
- 仅支持方法级别的切面:无法拦截字段访问或构造器调用。
- 性能开销:动态代理在运行时生成,会有轻微性能损耗。
- 自调用问题:类内部方法相互调用时,AOP可能失效。
七、总结
-
什么是Spring AOP:通过动态代理实现横切逻辑的解耦。
-
核心作用:提升代码复用性,简化日志、事务等通用逻辑。
-
实现方式:
- Spring原生代理:简单易用,适合大多数场景。
- AspectJ:功能强大,适合复杂需求。