Spring切点表达式
外观
Spring切点表达式[编辑 | 编辑源代码]
Spring切点表达式(Pointcut Expression)是Spring AOP(面向切面编程)中用于定义在哪些连接点(Join Point)上应用通知(Advice)的核心语法。它通过特定的表达式语言精确匹配目标方法或类,是AOP实现方法拦截的关键技术。
基本概念[编辑 | 编辑源代码]
切点表达式基于AspectJ切点表达式语言,Spring对其进行了部分扩展和简化。主要作用包括:
- 指定方法执行(如:`execution()`)
- 指定类型匹配(如:`within()`)
- 组合多个条件(如:`&&`, `||`, `!`)
表达式组成[编辑 | 编辑源代码]
一个完整的切点表达式通常包含以下部分: 其中:
- Designator:指示器(如`execution`)
- Pattern:匹配模式(如方法签名)
核心指示器详解[编辑 | 编辑源代码]
1. execution()[编辑 | 编辑源代码]
最常用的指示器,用于匹配方法执行。
语法结构:
execution(
[修饰符] 返回类型 [类路径].方法名(参数列表)
[throws 异常]
)
示例:
// 匹配com.example.service包下所有类的public方法
execution(public * com.example.service.*.*(..))
// 匹配UserService中以get开头的方法
execution(* com.example.service.UserService.get*(..))
// 匹配第一个参数为String的所有方法
execution(* *(String, ..))
2. within()[编辑 | 编辑源代码]
匹配特定类型(类/接口)内的所有连接点。
示例:
// 匹配Service包下的所有类
within(com.example.service.*)
// 匹配UserServiceImpl类
within(com.example.service.UserServiceImpl)
3. args()[编辑 | 编辑源代码]
基于参数类型的匹配。
示例:
// 匹配单个String参数的方法
args(String)
// 匹配第一个参数为Long的方法
args(Long,..)
组合表达式[编辑 | 编辑源代码]
通过逻辑运算符组合多个表达式:
运算符 | 示例 | 说明 | ||
---|---|---|---|---|
&& |
execution(* *(..)) && within(com.example..*) |
同时满足两个条件 | ||
|
execution(* save*(..)) |
execution(* update*(..)) | 满足任一条件 | |
! |
!within(com.example.test.*) |
排除test包 |
特殊语法[编辑 | 编辑源代码]
通配符使用[编辑 | 编辑源代码]
*
:匹配任意字符(除包分隔符外)..
:多重匹配
* 在包路径中:匹配当前包及其子包(如com.example..*
) * 在参数列表中:匹配任意数量参数(如(..)
)
bean()[编辑 | 编辑源代码]
Spring特有语法,通过Bean名称匹配:
// 匹配ID以Service结尾的Bean
bean(*Service)
// 排除userService
!bean(userService)
实际案例[编辑 | 编辑源代码]
日志切面示例[编辑 | 编辑源代码]
@Aspect
@Component
public class LoggingAspect {
// 拦截service包下所有public方法
@Pointcut("execution(public * com.example.service.*.*(..))")
private void serviceLayer() {}
@Before("serviceLayer()")
public void logMethodStart(JoinPoint jp) {
String methodName = jp.getSignature().getName();
System.out.println("【开始执行】" + methodName);
}
}
性能监控示例[编辑 | 编辑源代码]
@Aspect
@Component
public class PerformanceAspect {
// 组合表达式:public方法且带有@Monitor注解
@Pointcut("execution(public * *(..)) && @annotation(com.example.Monitor)")
private void monitoredMethod() {}
@Around("monitoredMethod()")
public Object measureTime(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object result = pjp.proceed();
long duration = System.currentTimeMillis() - start;
System.out.println(pjp.getSignature() + "执行耗时:" + duration + "ms");
return result;
}
}
表达式优化建议[编辑 | 编辑源代码]
1. 精确匹配优先:避免过度使用通配符
* 推荐:execution(* com.example.service.UserService.*(..))
* 不推荐:execution(* *.*(..))
2. 组合复用:通过@Pointcut
定义可重用的表达式
@Pointcut("within(com.example.service.*)")
public void inServiceLayer() {}
@Pointcut("execution(* save*(..))")
public void saveOperation() {}
// 组合使用
@Before("inServiceLayer() && saveOperation()")
public void beforeSave() { ... }
3. 性能考虑:bean()
表达式执行效率高于execution()
常见错误[编辑 | 编辑源代码]
错误表达式 | 问题 | 修正 |
---|---|---|
execution(* *(..)) |
拼写错误 | 改为execution
|
execution(public * *(..) |
缺少右括号 | 补全)
|
within(com.example.*) |
不会匹配子包 | 使用com.example..*
|
高级技巧[编辑 | 编辑源代码]
注解驱动切点[编辑 | 编辑源代码]
匹配带有特定注解的方法:
// 匹配带有@Transactional注解的方法
@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
public void transactionalMethod() {}
// 匹配参数带有@Valid注解的方法
@Pointcut("execution(* *(.., @javax.validation.Valid (*), ..))")
public void validParameter() {}
动态切点[编辑 | 编辑源代码]
通过条件判断实现运行时决策:
@Pointcut("execution(* com.example.dao.*.*(..)) && if()")
public static boolean dynamicPointcut(JoinPoint jp) {
return System.currentTimeMillis() % 2 == 0; // 条件示例
}
可视化理解[编辑 | 编辑源代码]
数学表达[编辑 | 编辑源代码]
切点匹配可以形式化为:
多个表达式的组合运算遵循布尔代数规则: