Spring AOP:切入点表达式

在本文中,我们将通过示例详细讨论 AOP 切入点表达式。

什么是切入点表达式
spring AOP中的切入点是基于一个称为切入点表达式的表达式来定义的。

这有助于根据表达式中指定的条件查找一组连接点。这些条件有助于定位特定的包、方法、类、注释和 bean。

execution(public int *.*.aopdemo.service.MyServiceImpl.getSum(int,int))

  • 切入点指示符 (PCD):PCD(上面以突出显示)有助于将方法、类、包、bean 和注释精确定位为连接点。
  • 表达式条件:它(非突出显示)有助于识别哪个特定类、方法、bean 或注释用作连接点。每个PCD具有不同的表达式条件语法。

不同的PCD及其表达式条件语法:

1、execution : 
最常用的 PCD,主要用于根据方法查找连接点。
语法: modifiers-pattern? return-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?

这里的修饰符模式(modifiers-pattern)、声明类型模式(declaring-type-pattern)和抛掷模式(throws-pattern)是可选的:

  • modifiers-pattern:指定要匹配的方法的访问修饰符。例如,公共、受保护、私有以及*(如有)。
  • return-type-pattern:指定要匹配的方法的返回类型,例如void、int、String、Object。* 如果是任何返回类型。
  • declaring-type-pattern:它指定声明方法的类型或完全限定类名,例如 com.sample.aopdemo.service.MyServiceImpl 和 * 表示任何类型。
  • name-pattern:它指定要匹配的方法的名称,例如,getSum 和* 表示任何方法名称。
  • param-pattern:指定要匹配的方法的参数类型,例如,int、String、* 表示任意参数类型,.. 表示任意数量的参数。
  • throws-pattern:它指定要匹配的方法抛出的异常的完整限定类名,例如 com.sample.aopdemo.exception.CoderStuffException 和 * 表示任何异常。

execution(public int com.sample.aopdemo.service.MyServiceImpl.getSum(int,int))
// 它将 aopdemo.service.MyServiceImpl 类中的 getSum 方法视为 joipoint,该方法接受 2 个 int 类型参数,并具有公共访问修饰符。
  
execution(public int com.sample.aopdemo.service.MyServiceImpl.getSum(..))
// 这与前面的示例相同,唯一不同的是 getSum 方法可以接受任意数量的参数。
  
execution(public int com.sample.aopdemo.service.MyServiceImpl.getException() throws com.sample.aopdemo.exception.CoderStuffException)
//在这里,我们将 com.sample.aopdemo.service.MyServiceImpl 类中的 getException() 方法视为连接点,该 getException 方法必须抛出 com.sample.aopdemo.exception.CoderStuffException 异常。

但也可以根据包和类来搜索连接点。示例:

execution(public * com.sample.aopdemo.controller.MyAppController.*())
//此表达式将 MyAppController 类中的所有方法都视为连接点。

execution(public * com.sample.aopdemo.controller.*.*())
//此表达式将 com.sample.aopdemo.controller 内所有类中的所有方法都视为连结点。

2、within :
它将指定软件包或类中的所有方法视为连接点。
语法:within(full qualified class name) 
示例:

within(com.sample.aopdemo.service.MyServiceImpl) 
// 它将 com.sample.aopdemo.MyServiceImpl 类中的所有方法都视为连结点。

within(com.sample.aopdemo.service.*) 
// 它将 com.sample.aopdemo.service 包内所有类中的所有方法都视为连结点。


3、bean 
它将指定 bean 中定义的所有方法都视为连接点。
语法:bean(bean name) 

bean(coderstuffservice)

这会将名为 coderstuffservice 的 bean 中的所有方法都视为连接点。

4、this 
它将所有方法都视为指定类实例中的连接点。
语法:this(class qualified name) 

this(com.sample.aopdemo.service.MyServiceIntf)

com.sample.aopdemo.service.MyServiceIntf 是一个接口,其实现由 com.sample.aopdemo.service.MyServiceImpl 类提供。因此它将 com.sample.aopdemo.service.MyServiceImpl 类中的所有方法都视为连结点。

5、annotation 
它将所有在其上有指定注解的方法都视为连接点。
语法:@annotation(annotation-type-pattern)

@annotation(org.springframework.web.bind.annotation.GetMapping)

@Pointcut
在 Spring AOP 中,我们还可以创建一个命名的快捷方式,以便在应用代码中重复使用。@Pointcut 注解有助于实现这一目的。使用 @Pointcut 注解,我们可以为一个点切口命名,并通过其名称重复使用它。示例

@Pointcut("execution(public int *.*.aopdemo.service.MyServiceImpl.*(..))")
public void logBeforeMethodExecutionPointCut(){
}

@Before(
"logBeforeMethodExecutionPointCut()")
public void logBeforeMethodExecution(JoinPoint joinPoint){
    String methodName = joinPoint.getSignature().toShortString();
    Object[] args = joinPoint.getArgs();
    log.info(
"Executing "+methodName+" with "+args.length+" arguments "+ Arrays.toString(args));
}

/***output****
Executing MyServiceImpl.getSum(..) with 2 arguments [4, 5]
Executing MyServiceImpl.multiply(..) with 2 arguments [4, 5]
Executing MyServiceImpl.getException() with 0 arguments []
*/

我们可以使用 &&、|| 和 .NET,在一个点切表达式中组合多个点切条件!操作符。示例 :

@Before("this(com.sample.aopdemo.service.MyServiceIntf) && !execution(public int com.sample.aopdemo.service.MyServiceImpl.getSum(int,int))"


在这里,它会考虑类中所有 com.sample.aopdemo.service.MyServiceIntf 类型的方法,但不会考虑 public int com.sample.aopdemo.service.MyServiceImpl.getSum(int,int) 方法。