讲解springAOP
Spring AOP 概述
- 定义:
- Spring AOP(Aspect-Oriented Programming)是 Spring 框架提供的面向切面编程模块,用于将横切关注点(如日志、事务、权限)与业务逻辑分离,提高代码模块化和可维护性。
- 核心思想:
- 通过切面(Aspect)在特定连接点(Join Point)上插入通知(Advice),实现功能增强。
- 实现方式:
- 基于代理模式(动态代理或 CGLIB),在运行时为目标对象生成代理。
核心点
- Spring AOP 通过代理实现非侵入式功能扩展,适合事务、日志等场景。
1. Spring AOP 核心概念
(1) 基本术语
- Aspect(切面):
- 封装横切逻辑的模块,如日志、事务。
- 包含 Advice 和 Pointcut。
- Join Point(连接点):
- 可插入切面的程序执行点,如方法调用、异常抛出。
- Spring AOP 只支持方法级别的连接点。
- Advice(通知):
- 切面在连接点执行的动作,类型包括:
- Before:方法前执行。
- After:方法后执行(无论成功或异常)。
- AfterReturning:方法成功返回后。
- AfterThrowing:方法抛异常后。
- Around:环绕方法执行,可控制调用。
- Pointcut(切点):
- 定义哪些连接点应用通知的规则。
- 例:
execution(* com.example.service.*.*(..))
。 - Target Object(目标对象):
- 被切面增强的原始对象。
- Proxy(代理):
- Spring AOP 为目标对象生成的代理对象。
- Weaving(织入):
- 将切面应用到目标对象的过程,Spring AOP 是运行时织入。
(2) 图示
[Client] --> [Proxy] --> [Advice: Before/After] --> [Target Method]
2. Spring AOP 实现原理
(1) 代理机制
- 动态代理(JDK Proxy):
- 若目标对象实现接口,使用 JDK 动态代理。
- 生成接口的代理类,调用
InvocationHandler
。 - CGLIB:
- 若无接口,使用 CGLIB 生成子类代理。
- 通过字节码操作,直接调用目标方法。
- 选择:
- Spring 自动选择,优先 JDK 代理。
- 可强制 CGLIB:
<aop:config proxy-target-class="true"/>
(2) 运行时织入
- 过程:
- Spring 容器启动,解析 AOP 配置(注解或 XML)。
- 根据 Pointcut 匹配目标方法。
- 生成代理对象,植入 Advice。
- 客户端调用代理,触发 Advice 和目标方法。
- 源码:
org.springframework.aop
包。- 核心类:
ProxyFactory
、AopProxy
。
3. Spring AOP 使用方式
(1) 基于注解
- 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 启用 AOP:
@SpringBootApplication
@EnableAspectJAutoProxy // 启用注解 AOP
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- 定义切面:
@Aspect
@Component
public class LogAspect {
// 切点:匹配所有 Service 方法
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
// 前置通知
@Before("serviceMethods()")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before: " + joinPoint.getSignature().getName());
}
// 后置通知
@AfterReturning(pointcut = "serviceMethods()", returning = "result")
public void logAfterReturning(Object result) {
System.out.println("AfterReturning: " + result);
}
// 环绕通知
@Around("serviceMethods()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Around: start");
Object result = joinPoint.proceed(); // 执行目标方法
System.out.println("Around: end");
return result;
}
}
- 目标类:
@Service
public class UserService {
public String getUser(int id) {
return "User: " + id;
}
}
(2) 基于 XML
- 配置:
<bean id="logAspect" class="com.example.LogAspect"/>
<aop:config>
<aop:aspect ref="logAspect">
<aop:pointcut id="serviceMethods" expression="execution(* com.example.service.*.*(..))"/>
<aop:before pointcut-ref="serviceMethods" method="logBefore"/>
<aop:after-returning pointcut-ref="serviceMethods" method="logAfterReturning" returning="result"/>
</aop:aspect>
</aop:config>
(3) 运行结果
- 调用
userService.getUser(1)
:
Around: start
Before: getUser
[执行 getUser]
AfterReturning: User: 1
Around: end
4. 常见应用场景
- 日志记录:
- 记录方法调用、参数、耗时。
- 事务管理:
@Transactional
基于 AOP 实现。- 例:自动开启/提交事务。
- 权限验证:
- 检查用户权限。
- 例:结合
@PreAuthorize
。 - 性能监控:
- 统计方法执行时间。
- 异常处理:
- 统一捕获异常,转换格式。
示例:事务 AOP
@Service
public class OrderService {
@Transactional
public void createOrder() {
// 数据库操作
}
}
- 内部:Spring 事务管理器通过 AOP 代理,自动处理
begin/commit/rollback
。
5. Spring AOP vs AspectJ
特性 | Spring AOP | AspectJ |
---|---|---|
织入方式 | 运行时(代理) | 编译时/加载时(字节码) |
连接点 | 仅方法 | 方法、字段、构造器等 |
性能 | 代理开销稍大 | 更高(直接修改字节码) |
复杂度 | 简单(Spring 集成) | 复杂(需额外工具) |
场景 | 企业应用(事务、日志) | 复杂切面(低级别控制) |
- Spring AOP 集成 AspectJ:
- 使用 AspectJ 的注解(如
@Pointcut
),但仍用 Spring 代理。
6. 注意事项
- 代理限制:
- 内部调用失效:
- 例:
@Service
public class UserService {
@Transactional
public void methodA() {
methodB(); // 不触发事务
}
@Transactional
public void methodB() {}
}
- 原因:内部调用不走代理。
- 解决:用 `AopContext.currentProxy()` 或注入自身。
- 性能开销:
- 代理增加调用层,Around 通知需谨慎。
- Pointcut 精确性:
- 过宽(如
execution(* *.*(..))
)影响性能。
7. 优缺点
优点
- 解耦:横切逻辑与业务分离。
- 可复用:切面可跨模块使用。
- 非侵入:目标代码无需修改。
缺点
- 限制:仅支持方法级别。
- 复杂性:调试代理逻辑困难。
- 性能:代理有微小开销。
8. 延伸与面试角度
- 与单点登录:
- AOP 可拦截请求,验证 JWT。
- 例:
@Before("execution(* com.example.controller.*.*(..))")
public void checkToken() {
// 验证 Authorization 头
}
- 与线程安全:
- AOP 可封装锁逻辑。
- 例:用
@Around
加ReentrantLock
。 - 实际应用:
- 电商:日志、事务、权限。
- 微服务:统一异常处理。
- 面试点:
- 问“原理”时,提代理和织入。
- 问“场景”时,提示例代码。
9. 总结
Spring AOP 通过代理模式实现面向切面编程,支持方法级别的 Before、After、Around 等通知,适用于日志、事务、权限等场景。基于 JDK 动态代理或 CGLIB,运行时织入,简单但功能有限。面试时,可提概念、代码或与 AspectJ 对比,结合单点登录等场景,展示理解深度。
如果您有具体场景或代码问题,请提供更多细节,我可以进一步优化答案!