精通 Spring AOP Execution 表达式:精准匹配你的切点

0 前言

AOP中,execution表达式是定义切点(Pointcut)的重要方式,切点决定了在哪些方法上应用增强(Advice,如前置通知、后置通知等 )。

1 表达式语法结构

execution表达式完整语法格式为:

execution([修饰符模式] 返回值类型模式 [类名模式]方法名模式(参数模式)[异常模式])

修饰符模式、异常模式较少用,常用部分:

  • execution():表达式主体,固定写法,标志这是一个用于匹配方法执行的切点表达式。
  • 返回值类型模式:用 * 表示匹配任意返回值类型;也可指定具体类型,如 void 表示无返回值 、int 表示返回整数类型等。如 execution(void com.example.service.UserService.add(..)) 表示匹配 UserService 类中 add 方法且返回值为 void
  • 类名模式:指定包名和类名相关信息。包名后若跟 .. 表示当前包及其子包;类名用 * 表示匹配所有类。如 com.example.service..* 表示 com.example.service 包及其子包下的所有类
  • 方法名模式: * 表示匹配所有方法名;也可指定特定方法名,如 get* 表示 get 开头方法
  • 参数模式: (..) 表示匹配任意参数数量和类型;也可具体指定,如 (int, String) 表示方法有两个参数,分别为 int 类型和 String 类型 ,(int,..) 表示第一个参数是 int 类型,后面可以跟任意数量和类型参数。

2 常用示例

匹配指定包下所有类的所有方法:

execution(* com.example.dao..*.*(..)) ,表示匹配 com.example.dao 包及其子包下所有类的所有方法,第一个 * 匹配任意返回值类型,第二个 * 匹配所有类,第三个 * 匹配所有方法名,(..) 匹配任意参数。

匹配指定类的所有方法:

execution(* com.example.service.UserService.*(..)) ,即匹配 com.example.service.UserService 类的所有方法 。

匹配指定接口所有实现类的方法:

execution(* com.example.dao.GenericDAO+.*(..)) ,+ 表示匹配 GenericDAO 接口的所有实现类的方法。

匹配特定方法名开头的方法:
execution(* save*(..)) ,表示匹配所有以 save 开头的方法,不限定返回值类型、类和参数。

3 多表达式组合

多个execution表达式之间可以通过逻辑运算符组合:

  • 或(|| 或 or ):表示满足其中一个表达式即可。如 execution(* com.example.service.UserService.add(..)) || execution(* com.example.service.UserService.delete(..)) ,表示匹配 UserService 类中的 add 方法或者 delete 方法。
  • 与(&& 或 and ):要求同时满足多个表达式。例如 execution(* com.example.service..*.*(..)) && args(String) ,表示匹配 com.example.service 包及其子包下所有类的方法,且方法参数包含 String 类型 。
  • 非(! 或 not ):对表达式取反。如 !execution(* com.example.service.UserService.get*(..)) ,表示匹配除了 UserService 类中以 get 开头方法之外的其他方法。

4 其他切点指示符

4.1 within表达式

主要用于根据类型(类或包)匹配连接点。即它可指定在哪些类或包中的方法执行时应用切面逻辑:

  • execution更侧重方法签名匹配
  • within侧重类或包的范围匹配
① 匹配指定包下的所有类
  • 语法:within(包名..*),其中 .. 表示当前包及其子包。
  • 示例:within(com.example.service..*) 表示匹配 com.example.service 包及其子包下所有类的所有方法。
  • 解释:这个表达式会让切面逻辑应用到该包及其子包中所有类的方法执行上。比如,在这个包下有 UserServiceOrderService 等类,这些类的所有方法都会被匹配。
② 匹配指定类
  • 语法:within(全限定类名)
  • 示例:within(com.example.service.UserService) 表示只匹配 com.example.service.UserService 类的所有方法。
  • 解释:只有 UserService 类中的方法执行时,切面逻辑才会被应用。

4.2 @annotation表达式

根据方法上是否存在特定注解来匹配连接点。当某方法标注指定注解,该方法执行就触发相应切面逻辑。

① 自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Loggable {
}
② 用 @annotation 匹配带注解的方法

语法:

@annotation(注解全限定名)

示例:

// 匹配所有带有 @Loggable 的方法
@annotation(com.example.annotation.Loggable)

示例:

@Aspect
@Component
public class LoggingAspect {

    @Pointcut("@annotation(com.example.annotation.Loggable)")
    public void loggableMethods() {}

    @Before("loggableMethods()")
    public void beforeLoggableMethod(JoinPoint joinPoint) {
        System.out.println("Before method: " + joinPoint.getSignature().getName());
    }
}
③ 在方法上用注解
import com.example.annotation.Loggable;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Loggable
    public void saveUser() {
        System.out.println("Saving user...");
    }
}

UserService#saveUser执行时,由于该方法有 @Loggable ,会触发 LoggingAspect 中的 beforeLoggableMethod 方法执行,实现在带特定注解的方法执行前添加切面逻辑。

4.3 小结

  • within 适用于按照类或包的范围来应用切面逻辑,是一种较为宽泛的匹配方式。
  • @annotation 适用于根据方法上的注解来精确控制切面逻辑的应用,更具针对性。这两种表达式可以和 execution 等其他表达式结合使用,以满足更复杂的切点定义需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值