spring aop 之 切点表达式

在面向切面编程中,切面是核心概念,正如在面向接口编程中,接口是核心概念一样,这里的接口和切面其实都是一个 Java 类,或者说具体表现形式就是个 Java 类。除了切面,切点和通知也是重要概念,我实在不懂为啥叫通知…这里所谓的通知其实就是回调,或者说监听器。而切点则类似 hook,或者触发点,当满足切点的事件发生时会回调各个通知。

首先,本文是上篇 spring aop - spring 面向切面编程的执行顺序 的续篇,我们接着上回继续说,下面呢,是一个标准的面向切面编程的例子:

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Aspect
@Slf4j
@Component
@Order(12)
public class LearnAopAspect {

    @Pointcut("@within(DataSource)")
    public void dataSource() {
    }

    @Before("dataSource()")
    public void changeDataSource(JoinPoint joinPoint) {
        log.info("===Before===");
    }

    @After("dataSource()")
    public void clearDataSource(JoinPoint joinPoint) {
        log.info("===After===");
    }

    @AfterReturning("dataSource()")
    public void afterReturn(JoinPoint joinPoint) {
        log.info("===AfterReturning===");
    }

    @AfterThrowing("dataSource()")
    public void afterThrow(JoinPoint joinPoint) {
        log.info("===AfterThrowing===");
    }

    @Around("dataSource()")
    public Object around(ProceedingJoinPoint joinPoint) {
        try {
            log.info("===Around===");
            Object object = joinPoint.proceed();
            log.info("===Around finish, result is {}", object);
            return "edit by aop and origin result is : " + object;
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }
}

@Aspect 是切面标记类,即声明这个类是个切面,同时,@Component 也是必须的,否则这个切面不会被调用,之后的 @Pointcut("@within(DataSource)") 定义了一个切点,这也是本文的核心,即切点表达式,这个方法的名字即切点的名字,这里是 dataSource(),后面的 @Before("dataSource()")、@After("dataSource()")、@AfterReturning("dataSource()") 等都是所谓的通知。

面向切面编程里,必须先定义一个切点,之后通过 @Before 等注解声明通知,当相关事件发生时,该通知会被调用,或者说被 标注的方法会被调用,除此之外,还有两个概念,即:JoinPoint、以及其子类 ProceedingJoinPoint,你可以称之为 连接点,通过连接点可以获取到被注释的方法以及类,还有入参等信息。

假设有个类:DataSource.java

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 注解,用于类和方法,指定使用的数据库,优先使用 方法上的注解,没有时从类上获取
 * name 对应 application.properties 里 设置 的 dynamic.datasoure.name
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)

public @interface DataSource {
    String name();
}

然后你在某个方法或者类里添加了 @DataSource(name="test") 的注解,如下:

@RestController
@RequestMapping("/test")
@Slf4j
@DataSource(name = "test")
public class Test {

    @RequestMapping("/test")
    public String test() {
        log.info("test method ...");
        return "success";
    }
}

那么,你可以通过如下代码读取到这里传入的 test,比如:

private DataSource findDataSource(JoinPoint joinPoint) {
    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    Method method = signature.getMethod();
    DataSource dataSource = method.getAnnotation(DataSource.class);
    if (dataSource == null) {
        try {
            dataSource = AnnotationUtils.findAnnotation(signature.getMethod().getDeclaringClass(), DataSource.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    return dataSource;
}

好了,现在大概已经说了 切面该如何定义,同时,还定义了一个切点,以及一些回调方法,同时给了一个例子。至于回调方法,除了 @Around 外,都很好理解,而 @Around 则是把实际调用的方法包裹起来,请看如下代码:

@Around("dataSource()")
public Object around(ProceedingJoinPoint joinPoint) {
    try {
        log.info("===Around===");
        Object object = joinPoint.proceed();
        log.info("===Around finish, result is {}", object);
        return "edit by aop and origin result is : " + object;
    } catch (Throwable e) {
        e.printStackTrace();
    }
    return null;
}

这里,调用 joinPoint.proceed(); 其实就是等于执行实际调用的方法,而且可以拿到其返回值,然后还可以修改返回值,修改后再返回…其余,比如@Before、@After 则很好理解了,本文不再赘述,我们来看看切点的表达式吧。除了上文中用到的 @Pointcut("@within(DataSource)") 外,还有很多别的标记,具体如下,比如 execution、@execution、this、target 等。

spring aop 支持的切入点标记

根据 spring 官网文档:aop-pointcuts-designators,完整的面向对象编程支持 call, get, set, preinitialization, staticinitialization, initialization, handler, adviceexecution, withincode, cflow, cflowbelow, if, @this 以及 @withincode 等,但是以上标记 spring aop 并不支持,如果使用了,将会抛出 IllegalArgumentException 异常。spring aop 支持以下切入点标记:

  • execution - for matching method execution join points, this is the primary pointcut designator you will use when working with Spring AOP
  • within - limits matching to join points within certain types (simply the execution of a method declared within a matching type when using Spring AOP)
  • this - limits matching to join points (the execution of methods when using Spring AOP) where the bean reference (Spring AOP proxy) is an instance of the given type
  • target - limits matching to join points (the execution of methods when using Spring AOP) where the target object (application object being proxied) is an instance of the given type
  • args - limits matching to join points (the execution of methods when using Spring AOP) where the arguments are instances of the given types
  • @target - limits matching to join points (the execution of methods when using Spring AOP) where the class of the executing object has an annotation of the given type
  • @args - limits matching to join points (the execution of methods when using Spring AOP) where the runtime type of the actual arguments passed have annotations of the given type(s)
  • @within - limits matching to join points within types that have the given annotation (the execution of methods declared in types with the given annotation when using Spring AOP)
  • @annotation - limits matching to join points where the subject of the join point (method being executed in Spring AOP) has the given annotation

以上内容引用自 spring 官方文档,目前我也一知半解,而网上的博客,无力吐槽!!!后面,详细解释其含义。这里主要说下 execution:

@Pointcut("execution(public * top.kpromise..mapper..*.*(..))")
public void dataSource() {

}

这里,第一个* 代表任意的返回值,top.kpromise 后面的.. 表示当前包及其子包,mapper 后面的 .. 同样表示当前包及其子包,之后的 * 表示任意类名,.*(..) 表示任何方法,括号里的 .. 表示任意参数,现在你应该明白,上面这个 execution 表达式的含义是:top.kpromise 及其子包下的 mapper 及其子包下的任意方法。

本博客若无特殊说明则由 full-stack-trip 原创发布
转载请点名出处:全栈之旅 > spring aop 之 切点表达式
本文地址:https://www.kpromise.top/spring-aop-pointcut-expression/

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注