AOP 切点表达式

在面向切面编程 (AOP) 中,我们希望将那些分散在各个模块中的通用功能(比如日志记录、事务管理、安全检查等,这些被称为“横切关注点”)抽离出来,形成独立的“切面 (Aspect)”。然后,我们需要告诉程序:“嘿,把这些切面里的逻辑,在某些特定的时候、特定的地方给我用上!”

“切点表达式”就是用来精确定义这些“特定的时候、特定的地方”的。

1. 什么是连接点 (Join Point)?

在理解切点之前,我们先要明白什么是“连接点”。

(通俗点说:)

连接点就像是程序执行过程中的一个个“岔路口”或者“事件点”。比如:

  • 一个方法的被调用时

  • 一个方法执行完成时

  • 一个对象被创建时

  • 一个异常被抛出时

  • 字段被访问或修改时

这些都是可以插入额外逻辑(也就是我们的“切面”逻辑)的潜在时机。

2. 什么是切点 (Pointcut)?

虽然连接点有很多,但我们通常并不想在所有连接点上都应用切面逻辑。我们只想在“我们感兴趣的”那些连接点上动手脚。

切点 (Pointcut) 就是一个或多个连接点的集合。它通过“切点表达式”来定义,用来精确地指定哪些连接点是我们关心的。

(打个比方:)

如果把程序执行流程比作一条高速公路,那么连接点就是高速公路上的每一个可能的出口、入口、服务区、收费站。而切点,就是你通过导航(切点表达式)设定的:“我只关心XX出口和YY服务区”。

3. 什么是切点表达式 (Pointcut Expression)?

切点表达式是一种具有特定语法的字符串,它描述了切点所匹配的连接点集合的规则。

最常用也是功能最强大的切点表达式语言是 AspectJ 的切点表达式语言。Spring AOP 框架就借鉴并支持了 AspectJ 的切点表达式语法。

4. AspectJ 切点表达式语法详解

AspectJ 的切点表达式主要由指示器 (Designator)模式 (Pattern) 组成。最常用的指示器是 execution,用于匹配方法的执行。

4.1 execution 指示器 (最常用)

execution 指示器用于匹配方法执行的连接点。其基本语法结构如下:

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

  • 修饰符模式? (可选)

    • 匹配方法的修饰符,如 public, protected, private

    • 可以使用 * 通配符,例如 public * 表示匹配所有 public 方法。

    • 如果省略,则匹配任意修饰符。

    • 示例: public (只匹配 public 方法)

  • 返回值类型模式 (必选)

    • 匹配方法的返回值类型。

    • 可以使用 * 通配符,表示匹配任意返回值类型。

    • 可以是具体的类型,如 String, void, com.example.User

    • 示例: String (返回值为 String 的方法), * (任意返回值类型), void (无返回值的方法)

  • 包名.类名模式 (部分可选,但通常会指定)

    • 匹配方法所在的包名和类名。

    • 可以使用 * 通配符:

      • com.example.*:匹配 com.example 包下的所有类。

      • com.example..*:匹配 com.example 包及其所有子包下的所有类 (两个点 .. 表示当前包及其子包)。

      • *Service:匹配所有以 Service 结尾的类名。

    • 示例: com.sky.service.EmployeeService, com.sky.mapper.*, com.sky..*.UserServiceImpl

  • 方法名模式 (必选)

    • 匹配方法名。

    • 可以使用 * 通配符,例如 set* 匹配所有以 set 开头的方法。

    • 示例: login, get*, * (所有方法)

  • 参数模式 (必选)

    • 匹配方法的参数列表。

    • ():匹配没有参数的方法。

    • (*):匹配有一个任意类型参数的方法。

    • (..):匹配有任意数量、任意类型参数的方法 (包括没有参数)。

    • (String):匹配有一个 String 类型参数的方法。

    • (String, *):匹配第一个参数是 String 类型,第二个参数是任意类型的方法。

    • 示例: (com.sky.dto.EmployeeDTO), (String, ..), ()

  • 异常模式? (可选)

    • 匹配方法声明抛出的异常类型。

    • 例如 throws java.io.IOException

    • 在实际应用中较少直接在 execution 中限制异常。

回顾您提供的例子:com.sky.mapper.*.*(..)

如果它用在 execution 指示器中,完整的写法通常是(假设我们不关心修饰符和返回值):

execution(* com.sky.mapper.*.*(..))

  • *:第一个 * 代表任意返回值类型。

  • com.sky.mapper:指定了包名。

  • 第一个 * (在 mapper. 之后):代表 com.sky.mapper 包下的所有类。

  • 第二个 * (在类名通配符之后):代表这些类中的所有方法。

  • (..):代表这些方法的参数是任意数量、任意类型的。

所以,execution(* com.sky.mapper.*.*(..)) 的含义是:匹配 com.sky.mapper 包中所有类的所有公共方法的执行。

4.2 其他常用指示器 (简介)

除了 execution,还有一些其他有用的指示器:

  • within(类型模式):

    • 匹配指定类型内部的所有连接点(通常是所有方法执行)。

    • 示例: within(com.sky.service.EmployeeService) (匹配 EmployeeService 类中的所有方法)

    • 示例: within(com.sky.service..*) (匹配 com.sky.service 包及其子包下所有类中的所有方法)

  • @annotation(注解类型):

    • 匹配标注了指定注解的方法的执行。

    • 示例: @annotation(com.sky.anno.MyLogAnnotation) (匹配所有标注了 @MyLogAnnotation 注解的方法)

  • @within(注解类型):

    • 匹配标注了指定注解的类内部的所有方法的执行。

    • 示例: @within(org.springframework.stereotype.Service) (匹配所有标注了 @Service 注解的类中的所有方法)

  • args(参数类型列表):

    • 匹配当前执行的方法传入的参数是指定类型的方法。

    • 示例: args(String, com.sky.dto.UserDTO)

  • @args(注解类型列表):

    • 匹配当前执行的方法传入的参数被指定注解标注的方法。

  • bean(bean的名称或ID):

    • Spring AOP 特有的,匹配特定名称的 Spring Bean 的方法。

    • 示例: bean(employeeServiceImpl)

    • 示例: bean(*Service) (匹配所有以 Service 结尾的Bean)

4.3 通配符
  • *:匹配任意字符序列(除了包分隔符 .)。可以用于返回值、包名的一部分、类名、方法名、参数类型。

  • ..

    • 在类名模式中,表示当前包及其所有子包。例如 com.example..

    • 在参数模式中,表示任意数量、任意类型的参数。例如 (..)

4.4 逻辑运算符

切点表达式可以使用逻辑运算符 && (与), || (或), ! (非) 来组合多个切点表达式,形成更复杂的切点定义。

  • 示例: execution(* com.sky.service.*.*(..)) && @annotation(com.sky.anno.Transactional)

    (匹配 com.sky.service 包下所有被 @Transactional 注解标注的方法)

5. 总结

切点表达式是 AOP 的核心组成部分,它赋予了我们精确选择连接点的能力。通过灵活运用各种指示器和通配符,我们可以定义出非常具体或非常广泛的切点,从而将切面逻辑准确地应用到需要的地方,实现代码的解耦和模块化。

最常用的就是 execution 指示器,掌握它的语法对于理解和使用 AOP 至关重要。

希望这个讲解能帮助您更好地理解切点表达式!

你可能感兴趣的:(苍穹外卖查缺补漏,java,学习)