在业务方法上打注解
package com.lib.service;@Service
public class BookService {@LogExecution(description = "查询图书")public Book query(int id) {return repo.findById(id);}@LogExecution(description = "借阅图书")public void borrow(int id) {// 模拟异常 50%if (Math.random() < 0.5) throw new RuntimeException("库存不足");}
}
5. 测试 & 观察
启动 Tomcat
浏览器访问
http://localhost:8080/library/book/1
多刷几次,控制台出现:
[AOP-LOG] BookService.query | desc=查询图书 | cost=3 ms | status=SUCCESS
[AOP-LOG] BookService.borrow | desc=借阅图书 | cost=1 ms | status=ERROR | msg=库存不足
6. 多切面执行顺序
再写一个 安全切面:
@Aspect
@Component
@Order(1) // 值越小越先执行
public class SecurityAspect {@Before("@annotation(LogExecution)")public void check() {System.out.println("[SECURITY] 权限校验通过");}
}
重启后观察日志顺序:
[SECURITY] 权限校验通过
[AOP-LOG] BookService.query ...
7. 底层实现速看
打开 IDEA → 双击 Shift → 输入
AnnotationAwareAspectJAutoProxyCreator
该类负责 后置处理器 生成代理。在
LoggingAspect.logAround()
第一行打断点,以 Debug 模式启动:
调用栈能看到CglibAopProxy
→DynamicAdvisedInterceptor
→MethodInterceptor
。结论:Spring 默认使用 CGLIB 代理类(无接口也能代理),若类实现接口也可强制用 JDK 动态代理(
@EnableAspectJAutoProxy(proxyTargetClass=false)
)。
8. 单元测试
@SpringJUnitConfig(AppConfig.class)
@EnableAspectJAutoProxy
class AopTests {@AutowiredBookService bookService;@Testvoid queryShouldPrintLog() {bookService.query(1); // 控制台应出现 AOP-LOG}
}