本文已收录在Github关注我,紧跟本系列专栏文章,咱们下篇再续!

  • 🚀 魔都架构师 | 全网30W技术追随者
  • 🔧 大厂分布式系统/数据中台实战专家
  • 🏆 主导交易系统百万级流量调优 & 车联网平台架构
  • 🧠 AIGC应用开发先行者 | 区块链落地实践者
  • 🌍 以技术驱动创新,我们的征途是改变世界!
  • 👉 实战干货:编程严选网

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 等其他表达式结合使用,以满足更复杂的切点定义需求。

    本文由博客一文多发平台 OpenWrite 发布!

JavaEdge
374 声望417 粉丝