Spring AOP使用AspectJ表达式拦截方法,核心是通过@Aspect切面配合@Pointcut和通知(如@Before、@Around)实现,依赖AspectJ语法在运行时匹配方法执行连接点,仅支持方法级表达式(如execution、@annotation、within、bean),不支持字段或构造器拦截。

Spring 使用 AspectJ 表达式拦截方法,核心是通过 @Aspect 切面配合 @Pointcut 和通知(如 @Before、@Around)实现。关键不在 Spring 自身解析表达式,而是借助 AspectJ 的切入点语法(Pointcut Expression),Spring AOP 在运行时代理时会识别并匹配这些表达式。
明确支持的 AspectJ 表达式类型
Spring AOP 只支持 AspectJ 的方法级切入点表达式,不支持字段访问、构造器、静态初始化块等完整 AspectJ 功能。常用表达式结构如下:
- execution(修饰符? 返回类型 类型声明.方法名(参数) 异常?) —— 最常用,用于匹配方法执行
- @annotation(全类名) —— 匹配标注了指定注解的方法
- within(包名..类型名) —— 匹配某个类或包下所有方法(注意:within 对接口无效,需作用于具体实现类)
-
bean(id 或 name 模式) —— Spring 特有,按 Bean 名称匹配(如
bean(*Service))
写一个可工作的 @Aspect 示例
确保项目已引入依赖(Maven):
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> </dependency>
定义切面:
@Aspect
@Component
public class LoggingAspect {
<p>// 匹配 com.example.service 包下所有 public 方法
@Pointcut("execution(public <em> com.example.service..</em>.*(..))")
public void serviceMethods() {}</p><p>@Before("serviceMethods()")
public void logBefore(JoinPoint joinPoint) {
System.out.println("调用: " + joinPoint.getSignature().toShortString());
}</p><p>// 匹配加了 @LogExecutionTime 注解的方法
@Pointcut("@annotation(com.example.annotation.LogExecutionTime)")
public void annotatedMethods() {}</p><p>@Around("annotatedMethods()")
public Object measureTime(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object result = pjp.proceed();
long end = System.currentTimeMillis();
System.out.println(pjp.getSignature() + " 耗时: " + (end - start) + "ms");
return result;
}
}常见表达式写法与避坑点
实际使用中容易出错的地方:
-
返回类型不能写成
*就万事大吉:比如execution(* com.example.dao.*.*())可能漏掉 void 方法(void 是有效返回类型),建议显式写..*或* * -
参数匹配要谨慎:
(..)匹配任意参数(包括 null),(*)只匹配单个任意参数,(String)严格匹配一个 String 参数 -
within 和 execution 区别要清楚:
within(com.example.service..*)匹配该包及其子包中所有类的任意方法;而execution(* com.example.service..*.*(..))更精准控制方法签名 -
Spring Boot 默认只启用 JDK 动态代理:若目标类无接口,需在启动类加
@EnableAspectJAutoProxy(proxyTargetClass = true)启用 CGLIB 代理
验证是否生效的小技巧
切面没触发?快速排查:
- 确认切面类被
@Component或@Bean注册,并且所在包被@ComponentScan扫描到 - 检查目标方法是否为 public(Spring AOP 不拦截 private/protected/package-private 方法)
- 在通知方法里加日志或断点,确认切面类本身被 Spring 加载(可在构造函数打日志)
- 开启 AOP 调试日志:
logging.level.org.springframework.aop=DEBUG


















