【Spring AOP】execution表达式,@annotation注解,@MyAspect(自定义注解),切面类

上面的代码中,我们一直在使用切点表达式来描述切点。下面我们来介绍一下切点表达式的语法。切点表达式常见有两种表达方式

  1. execution(......):根据方法的签名来匹配
  2. @annotation(......):根据注解匹配

execution 表达式

execution() 是最常用的切点表达式,用来匹配方法,语法为:

execution(<访问修饰符> <返回类型> <包名.类名.方法(方法参数)> <异常>)

其中,访问修饰符和异常可以省略image.png
切点表达式支持通配符表达:

  1. *:匹配任意字符,只匹配一个元素(返回类型,包,类名,方法或者方法参数)
    1. 包名使用 * 表示任意包(一层包使用一个 *
    2. 类名使用 * 表示任意类
    3. 返回值使用 * 表示任意返回值类型
    4. 方法名使用 * 表示任意方法
    5. 参数使用 * 表示一个任意类型的参数
  2. ..:匹配多个连续的任意符号,可以通配任意层级的包,或任意类型,任意个数的参数
    1. 使用 .. 配置包名,标识此包以及此包下的所有子包
    2. 可以是用 .. 配置参数,任意个任意类型的参数

切点表达式示例

TestController 下的 public 修饰,返回类型为 String 方法名为 t1,无参方法

execution(public String com.example.demo.controller.TestController.t1())

省略访问修饰符

execution(String com.example.demo.controller.TestController.t1())

匹配所有返回类型

execution(* com.example.demo.controller.TestController.t1())

匹配 TestController 下的所有无参方法

execution(* com.example.demo.controller.TestController.*())

匹配 TestController 下的所有方法

execution(* com.example.demo.controller.TestController.*(..))

匹配 controller 包下所有的类的所有方法

execution(* com.example.demo.controller.*.*(..))

匹配所有包下的 TestController

execution(* com..TestController.*(..))

匹配 com.example.demo 包下,子孙包下的所有类的所有方法

execution(* com.example.demo..*(..))

@annotation

execution 表达式更适用有规则的,如果我们要匹配多个无规则的方法呢?

  • 比如 TestController 中的 t1()UserController 中的 u1() 这两个方法
  • 这个时候我们使用 execution 这种切点表达式来描述就不是很方便了

我们可以借助自定义注解的方式以及另一种切点表达式 @annotation 来描述这一类的切点。实现步骤:

  1. 编写自定义注解
  2. 使用 @annotation 表达式来描述切点
  3. 在连接点的方法上添加自定义注解

准备测试代码

@RequestMapping("/test")  
@RestController  
public class TestController {  
    @RequestMapping("/t1")  
    public String t1() {  
        return "t1";  
    }  
  
    @RequestMapping("/t2")  
    public boolean t2() {  
        return true;  
    }  
}
@Controller  
@RequestMapping("/user")  
public class UserController {  
    @RequestMapping("/u1")  
    public String u1() {  
        return "t1";  
    }  
  
    @RequestMapping("/u2")  
    public String u2() {  
        return "u2";  
    }  
}

@MyAspect(自定义注解 )

创建一个注解类

  • 和创建 class 文件一样的流程,选择 Annotation 就可以了image.png|303
import java.lang.annotation.ElementType;  
import java.lang.annotation.Retention;  
import java.lang.annotation.RetentionPolicy;  
import java.lang.annotation.Target;  
  
@Target(ElementType.METHOD)  
@Retention(RetentionPolicy.RUNTIME)  
public @interface MyAspect {  
      
}

代码简单说明,了解即可

  1. @Target 标识了 Annotation 所修饰的对象范围,即该注解可以用在什么地方。常用取值:

    • ElementType.TYPE:用于描述类、接口(包括注解类型)或 enum 声明
    • ElementType.METHOD:描述方法
    • ElementType.PARAMETER:描述参数
    • ElementType.TYPE_USE:可以标注任意类型
  2. @RetentionAnnotation 被保留的时间长短,标明注解的声明周期

    1. RetentionPolicy.SOURCE:表示注解仅存在于源代码中,编译成字节码后会被丢弃。这意味着在运行时无法获取到该注解的信息,只能在编译时使用。比如 @SuppressWarnings 以及 lombok 提供的注解 @Data@Slf4j
    2. RetentionPolicy.CLASS:编译时注解。表示注解存在于源代码和字节码中,但在运行时会被丢弃。这意味着在编译时和字节码中可以通过反射获取到该注解的信息,但在实际运行时无法获取,通常用于一些框架和工具的注解
    3. RetentionPolicy.RUNTIME:运行时注解。表示注解存在于源代码,字节码和运行时中。这意味着在编译时,字节码中和实际运行时都可以通过反射获取到该注解的信息。通常用于一些需要再运行时处理的注解,如 Spring@Controller@ResponseBody

切面类

使用 @annotation 切点表达式定义切点,只对 @MyAspect 生效

切面代码如下:

@Slf4j  
@Aspect  
@Component  
public class MyAspectDemo {  
    // 前置通知  
    @Before("@annotation(com.example.demo.aspect.MyAspect)")  
    public void before() {  
        log.info("MyAspect -> before ...");  
    }  
      
    // 后置通知  
    @After("@annotation(com.example.demo.aspect.MyAspect)")  
    public void after() {  
        log.info("MyAspect -> after ...");  
    }  
}

添加自定义注解

TestController 中的 t1()UserController 中的 u1() 这两个方法上添加自定义注解 @Aspect,其他方法不添加

@MyAspect  
@RequestMapping("/t1")  
public String t1() {  
    return "t1";  
}
@MyAspect  
@RequestMapping("/u1")  
public String u1() {  
    return "u1";  
}

程序运行,测试接口: http://127.0.0.1:8080/test/t1
image.png

观察日志:image.png
可以看到,切面通知被执行了

继续测试:
http://127.0.0.1:8080/test/t2 ,切⾯通知未执⾏
http://127.0.0.1:8080/user/u1 , 切⾯通知执⾏

Spring AOP 的实现方式 #高频面试

  1. 基于注解 @Aspect(参考上面)
  2. 基于自定义注解(参考上面自定义注解 @annotation 部分)
  3. 基于 Spring API(通过 xml 配置的方式,自从 SpringBoot 广泛使用之后,这种方法几乎看不到了)
  4. 基于代理来实现(更遥远了,不建议使用)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值