系列文章第3篇(完结篇)| 共3篇
难度:⭐⭐⭐⭐ | 适合人群:想彻底搞懂Spring AOP的开发者
📝 系列回顾
经过前两篇的学习,我们已经掌握了:
- ✅ 第一篇: JDK动态代理和CGLIB动态代理原理
- ✅ 第二篇: @Aspect注解、5种通知类型、切点表达式
上期思考题解答:
Q1: 多个切面如何控制执行顺序?
A: 使用@Order注解或实现Ordered接口,数字越小越优先。(本篇详解)
Q2: @Around能完全替代其他4种通知吗?
A: 理论上可以,但不推荐。其他通知语义更清晰,代码更简洁。
Q3: 代理对象什么时候创建的?
A: 今天详细解答!
💥 开场:一次"见鬼"的AOP失效
时间: 周三晚上9点
地点: 家里(接到紧急电话)
事件: 线上日志丢失
运维: "今天下午的操作日志全没了,你的日志切面是不是有问题?"
我: "不可能啊,我测试过的..." 😰
紧急排查,发现代码:
@Service
public class UserService {
@Autowired
private UserDao userDao;
@Log("创建用户") // 有日志注解
public void createUser(User user) {
userDao.save(user);
}
@Log("批量创建") // 有日志注解
public void batchCreate(List<User> users) {
for (User user : users) {
this.createUser(user); // ← 问题在这里!
}
}
}
测试:
// 直接调用createUser - 日志正常
userService.createUser(user); // ✅ 日志记录
// 通过batchCreate调用 - 日志丢失
userService.batchCreate(users); // ❌ 日志丢失
我: "又是this调用的问题?" 😱
哈吉米(电话里): "对,this调用拿到的是原始对象,不是代理对象。AOP是通过代理实现的,绕过代理当然不生效!"
我: "那代理对象到底是怎么创建的?什么时候创建的?为什么this不是代理?" 🤔
南北绿豆(也在线): "这就要深入源码了,我给你讲讲..."
🔍 第一问:代理对象的创建时机
Bean生命周期中的位置
阿西噶阿西: "还记得Bean的11个生命周期步骤吗?"
1. 实例化Bean
2. 属性填充
3. BeanNameAware
4. BeanFactoryAware
5. ApplicationContextAware
6. BeanPostProcessor - Before
7. @PostConstruct
8. InitializingBean
9. init-method
10. BeanPostProcessor - After ← AOP代理在这里创建!
11. 销毁
代理创建在第10步:BeanPostProcessor的后置处理!
AbstractAutoProxyCreator源码
位置: org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
/**
* BeanPostProcessor后置处理方法
* 这里创建AOP代理
*/
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 如果不是提前暴露的Bean
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 如果需要代理,创建代理对象
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
/**
* 如果需要,包装成代理对象
*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 1. 已经处理过了,直接返回
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// 2. 不需要增强,直接返回
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// 3. 是基础设施类或应该跳过,直接返回
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 4. 获取所有适用于这个Bean的增强器(Advisor)
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(
bean.getClass(), beanName, null);
// 5. 如果有增强器,创建代理
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 创建代理对象
Object proxy = createProxy(
bean.getClass(),
beanName,
specificInterceptors,
new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy; // 返回代理对象
}
// 6. 没有增强器,返回原始Bean
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
}
创建代理的核心方法
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
// 1. 创建ProxyFactory
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
// 2. 决定使用JDK还是CGLIB
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true); // 使用CGLIB
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory); // 使用JDK
}
}
// 3. 构建增强器数组
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
// 4. 定制ProxyFactory
customizeProxyFactory(proxyFactory);
// 5. 创建代理对象
return proxyFactory.getProxy(getProxyClassLoader());
}
流程图
Bean创建流程
│
├─ 1-9步:实例化、属性填充、初始化
│
├─ 10. BeanPostProcessor后置处理
│ └─ AbstractAutoProxyCreator.postProcessAfterInitialization()
│ ├─ wrapIfNecessary()
│ │ ├─ 检查是否需要代理
│ │ ├─ 获取适用的Advisor
│ │ └─ createProxy()
│ │ ├─ 创建ProxyFactory
│ │ ├─ 决定JDK或CGLIB
│ │ ├─ 添加Advisor
│ │ └─ 生成代理对象
│ └─ 返回代理对象
│
└─ 注册到容器(注册的是代理对象)
关键: 容器中保存的是代理对象,不是原始对象!
💥 第二问:this调用问题的本质
问题演示
@Service
public class UserService {
@Log
public void method1() {
System.out.println("method1 执行");
this.method2(); // ← this调用
}
@Log
public void method2() {
System.out.println("method2 执行");
}
}
测试:
userService.method1();
输出:
>>> 【日志】method1 开始
method1 执行
method2 执行
<<< 【日志】method1 结束
(method2的日志没有记录!)
原理分析
哈吉米画图解释:
客户端代码:
userService.method1();
↓
userService变量指向的是?
↓
代理对象(UserService$$Proxy)
↓
代理逻辑执行
├─ 日志Before
├─ 调用method1()
│ └─ 进入原始对象的method1()
│ └─ this.method2() ← this是原始对象!
│ └─ 直接调用原始对象的method2()
│ └─ 没经过代理,AOP不生效! ❌
└─ 日志After
核心问题:
// 在UserService类内部
this → 原始对象(UserService@123)
// 不是
this → 代理对象(UserService$$Proxy@456)
解决方案
方案1:注入自己(推荐)
@Service
public class UserService {
@Autowired
@Lazy // 避免循环依赖
private UserService self; // 注入自己(代理对象)
@Log
public void method1() {
System.out.println("method1 执行");
self.method2(); // ← 通过代理调用
}
@Log
public void method2() {
System.out.println("method2 执行");
}
}
效果: ✅ AOP生效
方案2:使用AopContext
// 1. 启用exposeProxy
@EnableAspectJAutoProxy(exposeProxy = true)
@Configuration
public class AopConfig {
}
// 2. 获取当前代理对象
@Service
public class UserService {
@Log
public void method1() {
System.out.println("method1 执行");
// 获取当前代理对象
UserService proxy = (UserService) AopContext.currentProxy();
proxy.method2(); // 通过代理调用
}
@Log
public void method2() {
System.out.println("method2 执行");
}
}
效果: ✅ AOP生效
缺点: 代码侵入性强,不推荐
方案3:拆分成两个类(最佳)
@Service
public class UserService {
@Autowired
private UserHelper userHelper;
@Log
public void method1() {
System.out.println("method1 执行");
userHelper.method2(); // 调用另一个Bean
}
}
@Service
public class UserHelper {
@Log
public void method2() {
System.out.println("method2 执行");
}
}
效果: ✅ AOP生效,且代码结构更好
🐛 第三问:AOP失效的8种场景
场景1:方法不是public
哈吉米: "这是最容易忽略的!"
@Service
public class UserService {
@Log
private void createUser() { // ← private方法
System.out.println("创建用户");
}
@Log
protected void updateUser() { // ← protected方法
System.out.println("更新用户");
}
}
结果: ❌ AOP不生效
原因:
- JDK代理:接口方法必须是public
- CGLIB代理:只能代理public和protected方法,private无法代理
解决: 改成public方法
场景2:方法是final
@Service
public class UserService {
@Log
public final void createUser() { // ← final方法
System.out.println("创建用户");
}
}
结果: ❌ AOP不生效(CGLIB代理时)
原因: CGLIB通过继承实现,无法重写final方法
解决: 去掉final修饰符
场景3:方法是static
@Service
public class UserService {
@Log
public static void createUser() { // ← static方法
System.out.println("创建用户");
}
}
结果: ❌ AOP不生效
原因: 代理是对象级别的,static方法属于类,无法代理
解决: 改成实例方法
场景4:类内部调用(this调用)
@Service
public class UserService {
@Log
public void method1() {
this.method2(); // ← this调用
}
@Log
public void method2() {
// AOP不生效
}
}
结果: ❌ method2的AOP不生效
原因: 上面已经详细讲过
解决: 注入自己或拆分成两个类
场景5:Bean不是Spring管理的
// ❌ 没有@Service等注解
public class UserService {
@Log
public void createUser() {
// AOP不生效
}
}
// 手动new的对象
UserService userService = new UserService();
userService.createUser(); // AOP不生效
结果: ❌ AOP不生效
原因: AOP依赖Spring容器创建代理,手动new的对象不是代理
解决: 加上@Service,通过Spring容器获取
场景6:切点表达式写错
@Aspect
@Component
public class LogAspect {
// 包名写错了
@Before("execution(* com.example.services.*.*(..))") // ← services多了s
public void before() {
// 不会执行
}
}
// 实际包名是
package com.example.service; // ← service没有s
结果: ❌ AOP不生效
原因: 切点表达式匹配不到任何方法
解决: 检查切点表达式是否正确
场景7:异步方法中的AOP
@Service
public class UserService {
@Async
@Log // 日志切面
public void createUser() {
// 可能不生效或顺序错乱
}
}
结果: ⚠️ 可能不生效
原因: @Async也是通过代理实现,两个代理可能冲突
解决:
- 调整切面顺序(@Order)
- 确保@Async的代理在外层
场景8:在构造器中调用
@Service
public class UserService {
public UserService() {
this.init(); // ← 构造器中调用
}
@Log
public void init() {
// AOP不生效
}
}
结果: ❌ AOP不生效
原因: 构造器执行时,Bean还没创建完,代理还没生成
解决: 使用@PostConstruct
@Service
public class UserService {
@PostConstruct // 在Bean初始化后执行
@Log
public void init() {
// AOP生效 ✅
}
}
失效场景总结表
| 场景 | 原因 | 解决方案 |
|---|---|---|
| private方法 | 无法被代理 | 改成public |
| final方法 | CGLIB无法重写 | 去掉final |
| static方法 | 类方法无法代理 | 改成实例方法 |
| this调用 | 绕过代理对象 | 注入自己/拆分类 |
| 非Spring管理 | 不是代理对象 | 加@Service |
| 表达式错误 | 匹配不到方法 | 检查表达式 |
| 异步方法 | 代理冲突 | 调整顺序 |
| 构造器调用 | 代理未创建 | 用@PostConstruct |
🎯 第四问:多个切面的执行顺序
场景
// 日志切面
@Aspect
@Component
public class LogAspect {
@Around("@annotation(com.example.annotation.Log)")
public Object log(ProceedingJoinPoint pjp) throws Throwable {
System.out.println(">>> 日志 - Before");
Object result = pjp.proceed();
System.out.println("<<< 日志 - After");
return result;
}
}
// 权限切面
@Aspect
@Component
public class SecurityAspect {
@Around("@annotation(com.example.annotation.RequireRole)")
public Object checkRole(ProceedingJoinPoint pjp) throws Throwable {
System.out.println(">>> 权限 - Before");
Object result = pjp.proceed();
System.out.println("<<< 权限 - After");
return result;
}
}
// 性能切面
@Aspect
@Component
public class PerformanceAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object monitor(ProceedingJoinPoint pjp) throws Throwable {
System.out.println(">>> 性能 - Before");
Object result = pjp.proceed();
System.out.println("<<< 性能 - After");
return result;
}
}
问题: 这三个切面的执行顺序是什么?
默认顺序(随机)
南北绿豆: "如果不指定顺序,执行顺序是不确定的!"
可能的输出:
>>> 性能 - Before
>>> 日志 - Before
>>> 权限 - Before
[业务方法]
<<< 权限 - After
<<< 日志 - After
<<< 性能 - After
或者:
>>> 日志 - Before
>>> 性能 - Before
>>> 权限 - Before
[业务方法]
<<< 权限 - After
<<< 性能 - After
<<< 日志 - After
使用@Order指定顺序
@Aspect
@Component
@Order(1) // 优先级最高
public class SecurityAspect {
// 权限校验应该最先执行
}
@Aspect
@Component
@Order(2)
public class LogAspect {
// 日志其次
}
@Aspect
@Component
@Order(3)
public class PerformanceAspect {
// 性能监控最后
}
执行顺序:
>>> 权限 - Before (Order=1,最外层)
>>> 日志 - Before (Order=2,中间层)
>>> 性能 - Before (Order=3,最内层)
[业务方法]
<<< 性能 - After (Order=3,最内层)
<<< 日志 - After (Order=2,中间层)
<<< 权限 - After (Order=1,最外层)
规律:
- Before: 数字小的先执行(1→2→3)
- After: 数字小的后执行(3→2→1)
- 类似洋葱模型🧅
实现Ordered接口
@Aspect
@Component
public class LogAspect implements Ordered {
@Override
public int getOrder() {
return 2; // 返回优先级
}
@Around("...")
public Object log(ProceedingJoinPoint pjp) throws Throwable {
// ...
}
}
🔬 第五问:源码深度分析
Advisor、Advice、Pointcut关系
阿西噶阿西: "在Spring AOP源码中,有三个核心概念。"
// Advice(通知)
public interface Advice {
// 标记接口
}
// Pointcut(切点)
public interface Pointcut {
ClassFilter getClassFilter(); // 类过滤器
MethodMatcher getMethodMatcher(); // 方法匹配器
}
// Advisor(顾问)= Pointcut + Advice
public interface Advisor {
Advice getAdvice(); // 获取通知
}
public interface PointcutAdvisor extends Advisor {
Pointcut getPointcut(); // 获取切点
}
关系:
Advisor(顾问)
├─ Pointcut(在哪里增强)
└─ Advice(怎么增强)
@Aspect如何解析?
位置: org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
核心流程:
@Override
protected List<Advisor> findCandidateAdvisors() {
// 1. 查找实现了Advisor接口的Bean
List<Advisor> advisors = super.findCandidateAdvisors();
// 2. 查找@Aspect注解的Bean
if (this.aspectJAdvisorsBuilder != null) {
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
buildAspectJAdvisors()方法:
public List<Advisor> buildAspectJAdvisors() {
List<String> aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new ArrayList<>();
aspectNames = new ArrayList<>();
// 1. 获取所有Bean名称
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
// 2. 遍历所有Bean
for (String beanName : beanNames) {
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
// 3. 检查是否有@Aspect注解
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
// 4. 获取Aspect中的所有Advisor
List<Advisor> classAdvisors =
this.advisorFactory.getAdvisors(factory);
advisors.addAll(classAdvisors);
}
}
this.aspectBeanNames = aspectNames;
return advisors;
}
}
}
// 返回缓存的Advisors
// ...
}
getAdvisors()方法
@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
List<Advisor> advisors = new ArrayList<>();
// 1. 获取所有方法(排除@Pointcut方法)
for (Method method : getAdvisorMethods(aspectClass)) {
// 2. 为每个通知方法创建Advisor
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory,
advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
return advisors;
}
getAdvisor()方法
@Override
public Advisor getAdvisor(Method candidateAdviceMethod,
MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) {
// 1. 获取切点
AspectJExpressionPointcut expressionPointcut = getPointcut(
candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
if (expressionPointcut == null) {
return null;
}
// 2. 创建Advisor
return new InstantiationModelAwarePointcutAdvisorImpl(
expressionPointcut,
candidateAdviceMethod,
this,
aspectInstanceFactory,
declarationOrderInAspect,
aspectName);
}
解析流程:
@Aspect类
↓
扫描所有方法
↓
找到@Before、@After、@Around等注解
↓
解析切点表达式
↓
创建对应的Advisor
↓
Advisor = Pointcut + Advice
💻 第六问:完整实战案例
案例:操作日志记录
需求: 记录用户的所有操作到数据库
1. 自定义注解:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationLog {
/**
* 操作模块
*/
String module() default "";
/**
* 操作类型
*/
String type() default "";
/**
* 操作描述
*/
String desc() default "";
}
2. 操作日志实体:
public class OperationLogEntity {
private Long id;
private String username; // 操作人
private String module; // 模块
private String type; // 类型
private String desc; // 描述
private String method; // 方法名
private String params; // 参数
private String result; // 返回值
private Long costTime; // 耗时
private Date createTime; // 时间
// Getter和Setter...
}
3. 操作日志切面:
@Aspect
@Component
@Slf4j
public class OperationLogAspect {
@Autowired
private OperationLogService operationLogService;
/**
* 环绕通知:记录操作日志
*/
@Around("@annotation(operationLog)")
public Object recordLog(ProceedingJoinPoint pjp, OperationLog operationLog)
throws Throwable {
// 1. 构建日志对象
OperationLogEntity logEntity = new OperationLogEntity();
logEntity.setModule(operationLog.module());
logEntity.setType(operationLog.type());
logEntity.setDesc(operationLog.desc());
// 2. 获取方法信息
String className = pjp.getTarget().getClass().getSimpleName();
String methodName = pjp.getSignature().getName();
logEntity.setMethod(className + "." + methodName);
// 3. 获取参数
Object[] args = pjp.getArgs();
logEntity.setParams(JSON.toJSONString(args));
// 4. 获取当前用户(从SecurityContext或Session)
String username = getCurrentUsername();
logEntity.setUsername(username);
// 5. 执行方法,记录耗时
long start = System.currentTimeMillis();
Object result = null;
try {
result = pjp.proceed();
// 6. 记录返回值
logEntity.setResult(JSON.toJSONString(result));
} catch (Exception e) {
// 7. 记录异常
logEntity.setResult("异常:" + e.getMessage());
throw e;
} finally {
long end = System.currentTimeMillis();
logEntity.setCostTime(end - start);
logEntity.setCreateTime(new Date());
// 8. 异步保存日志(不影响主流程)
CompletableFuture.runAsync(() -> {
operationLogService.save(logEntity);
});
}
return result;
}
private String getCurrentUsername() {
// 实际从SecurityContext获取
return "admin";
}
}
4. 使用:
@Service
public class UserService {
@OperationLog(
module = "用户管理",
type = "创建",
desc = "创建新用户"
)
public User createUser(User user) {
// 保存用户
userDao.save(user);
return user;
}
@OperationLog(
module = "用户管理",
type = "删除",
desc = "删除用户"
)
public void deleteUser(Long userId) {
userDao.delete(userId);
}
@OperationLog(
module = "用户管理",
type = "更新",
desc = "更新用户信息"
)
public User updateUser(User user) {
userDao.update(user);
return user;
}
}
5. 测试:
userService.createUser(new User("张三", "zhangsan@example.com"));
数据库中的日志:
SELECT * FROM operation_log;
| id | username | module | type | desc | method | params | result | costTime | createTime |
|----|----------|--------|------|------|--------|--------|--------|----------|------------|
| 1 | admin | 用户管理 | 创建 | 创建新用户 | UserService.createUser | [{"name":"张三","email":"zhangsan@example.com"}] | {"id":1,"name":"张三"} | 15 | 2024-01-20 10:30:00 |
完美! ✨
🎉 系列总结
三篇文章回顾
第一篇:代理模式与动态代理
- ✅ 静态代理vs动态代理
- ✅ JDK动态代理原理
- ✅ CGLIB动态代理原理
- ✅ 两者对比与选择
- ✅ 手写简易版代理框架
第二篇:@Aspect注解与通知类型
- ✅ AOP核心概念(6个)
- ✅ 5种通知类型详解
- ✅ 通知执行顺序
- ✅ 切点表达式
- ✅ 实战案例(日志、权限、性能)
第三篇:代理创建时机与失效场景
- ✅ 代理创建时机(BeanPostProcessor后置处理)
- ✅ AbstractAutoProxyCreator源码
- ✅ AOP失效的8种场景
- ✅ this调用问题详解
- ✅ 多个切面的执行顺序
- ✅ 完整的操作日志案例
学习路线
Level 1: 会用AOP ⭐
└─ 知道@Aspect、@Before等注解
Level 2: 理解原理 ⭐⭐
└─ 理解动态代理
Level 3: 掌握通知 ⭐⭐⭐
└─ 掌握5种通知类型和切点表达式
Level 4: 深入源码 ⭐⭐⭐⭐
└─ 理解代理创建时机和过程
Level 5: 精通AOP ⭐⭐⭐⭐⭐
└─ 能解决所有AOP问题,设计复杂切面
恭喜你,完成这个系列,你已经达到Level 4! 🎉
核心知识图谱
Spring AOP
├─ 底层技术
│ ├─ JDK动态代理(基于接口)
│ └─ CGLIB代理(基于继承)
│
├─ 核心概念
│ ├─ 切面(Aspect)
│ ├─ 切点(Pointcut)
│ ├─ 通知(Advice)
│ ├─ 连接点(Join Point)
│ ├─ 目标对象(Target)
│ └─ 织入(Weaving)
│
├─ 通知类型
│ ├─ @Before
│ ├─ @After
│ ├─ @AfterReturning
│ ├─ @AfterThrowing
│ └─ @Around
│
├─ 代理创建
│ ├─ BeanPostProcessor后置处理
│ ├─ AbstractAutoProxyCreator
│ └─ ProxyFactory
│
└─ 常见问题
├─ this调用失效
├─ private/final/static方法失效
├─ 非Spring管理失效
└─ 切面顺序控制
💡 最佳实践建议
1. 切面设计原则
// ✅ 推荐:职责单一
@Aspect
@Component
public class LogAspect {
// 只负责日志
}
@Aspect
@Component
public class SecurityAspect {
// 只负责权限
}
// ❌ 不推荐:职责混乱
@Aspect
@Component
public class CommonAspect {
// 又做日志,又做权限,又做性能监控...
}
2. 切点表达式优化
// ✅ 推荐:使用@Pointcut复用
@Aspect
@Component
public class LogAspect {
@Pointcut("@annotation(com.example.annotation.Log)")
public void logMethods() {}
@Before("logMethods()") // 复用
public void before() {}
@After("logMethods()") // 复用
public void after() {}
}
// ❌ 不推荐:重复写表达式
@Before("@annotation(com.example.annotation.Log)")
@After("@annotation(com.example.annotation.Log)")
3. 通知类型选择
// ✅ 推荐:语义清晰
@Before("...") // 只需要前置处理
public void before() {}
// ❌ 不推荐:滥用@Around
@Around("...")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
// 前置处理
return pjp.proceed(); // 后置什么都不做
}
4. 异常处理
// ✅ 推荐:Around中完整处理异常
@Around("...")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
try {
return pjp.proceed();
} catch (BusinessException e) {
// 业务异常:记录日志,继续抛出
log.error("业务异常", e);
throw e;
} catch (Exception e) {
// 系统异常:记录日志,包装后抛出
log.error("系统异常", e);
throw new SystemException("系统错误", e);
}
}
5. 性能考虑
// ✅ 推荐:避免在切面中执行耗时操作
@Around("...")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
// 耗时操作异步执行
CompletableFuture.runAsync(() -> {
saveLog(...); // 异步保存日志
});
return pjp.proceed();
}
// ❌ 不推荐:同步执行耗时操作
@Around("...")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
saveLog(...); // 同步保存,影响性能
return pjp.proceed();
}
💬 写在最后
从动态代理到@Aspect注解,再到源码剖析,我们完整学习了Spring AOP的全部核心内容:
- 🎭 理解了JDK和CGLIB两种代理方式
- 🎯 掌握了5种通知类型和切点表达式
- 🔬 深入了代理创建时机和源码实现
- 🐛 学会了AOP失效场景的排查和解决
- 💻 完成了多个实战案例
这个系列到这里就完结了!
如果这个系列对你有帮助,请:
- 👍 点赞支持
- ⭐ 收藏备用
- 🔄 转发分享
- 💬 评论交流
感谢一路陪伴,期待下个系列再见! 👋