首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【Spring AOP】execution表达式,@annotation注解,@MyAspect(自定义注解),切面类

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

作者头像
椰椰椰耶
发布2025-07-21 09:05:19
发布2025-07-21 09:05:19
15700
代码可运行
举报
文章被收录于专栏:学习学习
运行总次数:0
代码可运行

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

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

execution 表达式

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

代码语言:javascript
代码运行次数:0
运行
复制
execution(<访问修饰符> <返回类型> <包名.类名.方法(方法参数)> <异常>)

其中,访问修饰符和异常可以省略

image.png
image.png

切点表达式支持通配符表达:

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

切点表达式示例

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

代码语言:javascript
代码运行次数:0
运行
复制
execution(public String com.example.demo.controller.TestController.t1())

省略访问修饰符

代码语言:javascript
代码运行次数:0
运行
复制
execution(String com.example.demo.controller.TestController.t1())

匹配所有返回类型

代码语言:javascript
代码运行次数:0
运行
复制
execution(* com.example.demo.controller.TestController.t1())

匹配 TestController 下的所有无参方法

代码语言:javascript
代码运行次数:0
运行
复制
execution(* com.example.demo.controller.TestController.*())

匹配 TestController 下的所有方法

代码语言:javascript
代码运行次数:0
运行
复制
execution(* com.example.demo.controller.TestController.*(..))

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

代码语言:javascript
代码运行次数:0
运行
复制
execution(* com.example.demo.controller.*.*(..))

匹配所有包下的 TestController

代码语言:javascript
代码运行次数:0
运行
复制
execution(* com..TestController.*(..))

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

代码语言:javascript
代码运行次数:0
运行
复制
execution(* com.example.demo..*(..))

@annotation

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

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

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

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

准备测试代码

代码语言:javascript
代码运行次数:0
运行
复制
@RequestMapping("/test")  
@RestController  
public class TestController {  
    @RequestMapping("/t1")  
    public String t1() {  
        return "t1";  
    }  
  
    @RequestMapping("/t2")  
    public boolean t2() {  
        return true;  
    }  
}
代码语言:javascript
代码运行次数:0
运行
复制
@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
image.png|303
代码语言:javascript
代码运行次数:0
运行
复制
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 生效

切面代码如下:

代码语言:javascript
代码运行次数:0
运行
复制
@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,其他方法不添加

代码语言:javascript
代码运行次数:0
运行
复制
@MyAspect  
@RequestMapping("/t1")  
public String t1() {  
    return "t1";  
}
代码语言:javascript
代码运行次数:0
运行
复制
@MyAspect  
@RequestMapping("/u1")  
public String u1() {  
    return "u1";  
}

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

image.png
image.png

观察日志:

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. 基于代理来实现(更遥远了,不建议使用)
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-07-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • execution 表达式
    • 切点表达式示例
  • @annotation
    • @MyAspect(自定义注解 )
    • 切面类
    • 添加自定义注解
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档