跳转到内容

Spring切点表达式

来自代码酷

Spring切点表达式[编辑 | 编辑源代码]

Spring切点表达式(Pointcut Expression)是Spring AOP(面向切面编程)中用于定义在哪些连接点(Join Point)上应用通知(Advice)的核心语法。它通过特定的表达式语言精确匹配目标方法或类,是AOP实现方法拦截的关键技术。

基本概念[编辑 | 编辑源代码]

切点表达式基于AspectJ切点表达式语言,Spring对其进行了部分扩展和简化。主要作用包括:

  • 指定方法执行(如:`execution()`)
  • 指定类型匹配(如:`within()`)
  • 组合多个条件(如:`&&`, `||`, `!`)

表达式组成[编辑 | 编辑源代码]

一个完整的切点表达式通常包含以下部分: Pointcut=Designator(Pattern) 其中:

  • 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; // 条件示例
}

可视化理解[编辑 | 编辑源代码]

graph TD A[切点表达式] --> B[execution] A --> C[within] A --> D[args] A --> E[annotation] B --> F["execution(* com.example..*(..))"] C --> G["within(com.example.service.*)"] D --> H["args(java.io.Serializable)"] E --> I["@annotation(org.springframework.transaction.annotation.Transactional)"]

数学表达[编辑 | 编辑源代码]

切点匹配可以形式化为: P(m)={true,当方法m满足表达式条件false,否则

多个表达式的组合运算遵循布尔代数规则: P1(m)P2(m)AND运算 P1(m)P2(m)OR运算