您的位置:

切面表达式:细节决定成败

一、切面表达式注解

注解是Java中非常重要的一种语法标记,Spring AOP中也通过注解的方式来定义切面。在定义切面时,可以使用@Aspect注解表示一个切面类,也可以使用@Before、@After、@Around等注解表示具体的切面执行时机。下面是一个使用注解定义切面并且在方法执行之前和之后打印日志的示例:

@Aspect
public class LogAspect {
    @Before("execution(* com.example.demo.service.*.*(..))")
    public void beforeLog() {
        System.out.println("Before invoking method.");
    }
    @After("execution(* com.example.demo.service.*.*(..))")
    public void afterLog() {
        System.out.println("After invoking method.");
    }
}

在该示例中,通过@Before和@After注解来表示在目标方法执行之前和之后执行。而@Aspect注解则表示该类是一个切面类。

二、Spring切面表达式

Spring AOP支持切点表达式,可以使用表达式来匹配满足某些条件的方法进行切面的处理。Spring AOP中使用AspectJ的切点表达式,其语法与AspectJ基本一致,但也有一些不同之处。下面是一些常用的切点表达式:

  • execution(* com.example.demo.service.*.*(..)):匹配com.example.demo.service包中所有类的所有方法。
  • execution(public * com.example.demo.service.UserService.*(..)):匹配com.example.demo.service.UserService接口中所有public方法。
  • execution(* com.example.demo.service.UserService+.*(..)):匹配com.example.demo.service.UserService接口及其所有实现类的所有方法。
  • @annotation(com.example.demo.annotation.Log):匹配所有被@Log注解标记的方法。
  • within(com.example.demo.service.*):匹配com.example.demo.service包中所有类的所有方法,但不包括子包中的。

三、切面表达式参数使用

除了上述示例中的切点表达式,还可以在切点表达式中使用参数。参数有两种类型:JoinPoint和ProceedingJoinPoint。JoinPoint表示方法的执行时机,而ProceedingJoinPoint则表示带有返回值的方法执行时机。下面是一个示例:

@Aspect
public class LogAspect {
    @AfterReturning(value = "@annotation(com.example.demo.annotation.Log)", returning = "result")
    public void afterLog(JoinPoint joinPoint, Object result) {
        System.out.println("After invoking method: " + joinPoint.getSignature().getName() + ", result: " + result);
    }
    @Around("@annotation(com.example.demo.annotation.Log)")
    public Object aroundLog(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("Before invoking method: " + proceedingJoinPoint.getSignature().getName());
        Object result = proceedingJoinPoint.proceed();
        System.out.println("After invoking method: " + proceedingJoinPoint.getSignature().getName() + ", result: " + result);
        return result;
    }
}

在该示例中,@AfterReturning注解表示在方法执行后打印方法名和返回值。而@Around注解则表示在方法执行前后打印日志,并且通过proceed()方法继续执行原始方法,并且可以获取返回值。

四、切面表达式生效

在Spring AOP中,只有通过@Pointcut注解声明的方法才能被其他切面方法调用。下面是一个示例:

@Aspect
public class LogAspect {
    @Pointcut("@annotation(com.example.demo.annotation.Log)")
    public void logPointcut() {}
    @AfterReturning(value = "logPointcut()", returning = "result")
    public void afterLog(JoinPoint joinPoint, Object result) {
        System.out.println("After invoking method: " + joinPoint.getSignature().getName() + ", result: " + result);
    }
}

在该示例中,通过@Pointcut注解声明一个logPointcut()方法,在其他切面方法中可以使用logPointcut()方法。

五、二元函数切面的表达式

二元函数切面表示同时匹配两个或多个切点的情况。在Spring AOP中,可以使用&&、||和!符号来实现二元函数切点。下面是一个示例:

@Aspect
public class LogAspect {
    @AfterReturning(value = "logPointcut() && execution(* com.example.demo.service.UserService.*(..))", returning = "result")
    public void afterLog(JoinPoint joinPoint, Object result) {
        System.out.println("After invoking UserService method: " + joinPoint.getSignature().getName() + ", result: " + result);
    }
}

在该示例中,使用&&符号同时匹配logPointcut()和UserService的所有方法。

六、AOP切面表达式书面表达格式选取

在书写切面表达式时,应注意语法的正确性和表达式的简洁性。下面是一些书写表达式时需要注意的规则:

  • 尽量使用通配符,减少代码量。
  • 使用括号对表达式进行分组,使运算符优先级更清晰。
  • 使用点号分割包名和类名,便于阅读。
  • 使用注解、参数等限制条件来过滤切点,提高代码的可读性。