辽源市网站建设_网站建设公司_加载速度优化_seo优化
2026/1/12 20:55:19 网站建设 项目流程

一、先明确核心概念

  • @Pointcut:定义切入点,即指定 AOP 通知(如 @Before)要作用于哪些方法;
  • execution():最常用的切入点表达式语法,格式为:execution(修饰符? 返回值 包名.类名.方法名(参数类型) throws异常?)其中?表示可选,核心是返回值、包类路径、方法名、参数这四部分。
  • @Before("pt()"):表示在pt()切入点匹配的方法执行之前,执行before()方法中的逻辑(打印日志)。

二、逐行拆解注释 / 未注释的切入点表达式

1. 精准匹配(指定全路径 + 方法 + 参数)
//@Pointcut("execution(public void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))")
  • 含义:匹配com.itheima.service.impl.DeptServiceImpl类中,public修饰、返回值为void、方法名delete、参数为Integer类型的方法;
  • 特点:最精准,修饰符(public)、全类名、方法名、参数都明确指定,仅匹配这一个方法。
//@Pointcut("execution(void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))")
  • 含义:去掉了public修饰符,匹配该类中任意修饰符(public/private/protected)的delete(Integer)方法;
  • 特点:修饰符可选,省略后匹配所有修饰符的同名方法。
//@Pointcut("execution(void delete(java.lang.Integer))") //包名.类名不建议省略
  • 含义:省略了包名和类名,理论上匹配所有类中返回值void、方法名delete、参数Integer的方法;
  • 问题:范围太广,容易匹配到非目标方法,因此注释中提示 “包名。类名不建议省略”。
//@Pointcut("execution(void com.itheima.service.DeptService.delete(java.lang.Integer))")
  • 含义:匹配接口DeptService中的delete(Integer)方法;
  • 关键:Spring AOP 中,即使切入点写的是接口方法,也会匹配接口的实现类(如 DeptServiceImpl)的对应方法,这是 AOP 面向接口编程的特性。
2. 通配符匹配(*、..)
//@Pointcut("execution(void com.itheima.service.DeptService.*(java.lang.Integer))")
  • 含义:匹配DeptService接口中所有方法名、返回值void、参数为Integer的方法;
  • 通配符*:表示 “任意”,此处替代方法名,即DeptService中所有返回void且参数是Integer的方法都会被匹配。
//@Pointcut("execution(* com.*.service.DeptService.*(*))")
  • 拆解
    • 第一个*:返回值任意(void/int/Object 等);
    • com.*.service:匹配com任意一级子包service包(如com.abc.servicecom.def.service,但不匹配com.abc.def.service);
    • 第二个*:方法名任意;
    • (*):参数为任意一个参数(类型不限);
  • 含义:匹配com.xxx.service.DeptService中所有 “单个参数” 的方法(返回值任意)。
//@Pointcut("execution(* com.itheima.service.*Service.delete*(*))")
  • 拆解
    • *Service:匹配service包下所有以 Service 结尾的类 / 接口(如 DeptService、EmpService);
    • delete*:匹配方法名以delete开头的方法(如 delete、deleteById、deleteBatch);
    • (*):单个任意类型参数;
  • 含义:匹配com.itheima.service包下所有 Service 接口 / 类中,以delete开头、带单个参数的方法(返回值任意)。
//@Pointcut("execution(* com.itheima.service.DeptService.*(..))")
  • 拆解
    • *(..)*是方法名任意,..表示 “任意个数、任意类型的参数”(0 个、1 个、多个都匹配);
  • 含义:匹配DeptService所有方法(无论返回值、无论参数),这是匹配某个接口 / 类所有方法的常用写法。
//@Pointcut("execution(* com..DeptService.*(..))")
  • 拆解com..DeptService..表示 “com 包下任意层级的子包”(如com.service.DeptServicecom.itheima.service.DeptServicecom.itheima.abc.service.DeptService);
  • 含义:匹配 com 包下任意层级中DeptService类 / 接口的所有方法。
//@Pointcut("execution(* com..*.*(..))")
  • 含义:匹配com包下所有类、所有方法(返回值任意、参数任意);
  • 特点:范围极广,几乎匹配项目中所有 com 包下的方法,一般仅用于测试。
//@Pointcut("execution(* *(..))") //慎用
  • 含义:匹配整个项目中所有方法(无任何包、类、方法限制);
  • 提示慎用:会导致 AOP 通知作用于所有方法,包括 Spring 内置方法(如 Controller、Service、甚至框架自身的方法),性能极差且易出问题。
3. 最终生效的组合表达式(重点)
@Pointcut("execution(* com.itheima.service.DeptService.list()) || " + "execution(* com.itheima.service.DeptService.delete(java.lang.Integer))") private void pt(){}
  • 核心语法||:逻辑 “或”,表示匹配任意一个表达式的方法;
  • 拆解
    1. execution(* com.itheima.service.DeptService.list())
      • 匹配DeptService接口中无参数、方法名 list的方法(*表示返回值任意);
    2. execution(* com.itheima.service.DeptService.delete(java.lang.Integer))
      • 匹配DeptService接口中方法名 delete、参数为 Integer的方法(返回值任意);
  • 最终效果@Before通知会在DeptService.list()DeptService.delete(Integer)这两个方法执行前触发,打印日志MyAspect6 ... before ...

三、核心语法规则总结

语法符号含义示例
*通配符,匹配 “任意一个”*(返回值任意)、delete*(方法名以 delete 开头)
..通配符,匹配 “任意层级 / 任意个数”com..Service(com 下任意层级的 Service 类)、(..)(任意参数)
``逻辑或,匹配任意一个表达式表达式 A表达式 B
&&逻辑与,匹配同时满足的表达式表达式 A && 表达式 B
!逻辑非,匹配不满足的表达式! 表达式 A

四、实战注意事项

  1. 精准匹配优先:生产环境尽量使用全路径 + 方法名 + 参数的精准表达式,避免通配符范围过大;
  2. 面向接口编程:切入点写接口方法(如 DeptService),会自动匹配实现类(DeptServiceImpl)的方法,符合 Spring 规范;
  3. 组合表达式:通过||/&&/!可以灵活组合多个切入点,满足复杂匹配需求;
  4. 访问修饰符:execution 表达式中修饰符(public/private)可选,省略时匹配所有修饰符。

总结

  1. 这段代码的核心是通过@Pointcut定义切入点,最终仅匹配DeptService.list()DeptService.delete(Integer)两个方法;
  2. execution()表达式的核心是 “返回值 + 包类路径 + 方法名 + 参数”,配合*/..通配符和||逻辑符可灵活匹配;
  3. 通配符使用需谨慎,范围过大(如execution(* *(..)))会导致性能问题,生产环境优先精准匹配;
  4. @Before("pt()")表示在匹配的方法执行前,执行before()方法的日志打印逻辑。

Spring AOP 中 @annotation 切入点表达式详解

@annotation是 Spring AOP 中另一种核心的切入点表达式语法,它的核心逻辑是:匹配所有标注了指定注解的方法,相比execution()(按方法路径 / 参数匹配),@annotation更灵活、更贴合 “面向业务场景” 的切入点定义(比如标记 “需要日志”“需要权限校验” 的方法)。

一、@annotation 基本语法

1. 核心格式
@Pointcut("@annotation(注解的全限定类名)") // 简写(若注解和切面类在同一包,可省略包名) @Pointcut("@annotation(自定义注解类名)")
2. 核心逻辑

只要目标方法上标注了指定的注解,该方法就会被切入点匹配,AOP 通知(@Before/@After 等)就会作用于该方法。

二、完整示例(从自定义注解到切面实现)

我们通过 “给方法加日志注解,切面拦截所有带该注解的方法” 这个场景,完整演示@annotation的用法:

步骤 1:定义自定义注解(标记需要拦截的方法)
// 自定义注解:标记需要打印日志的方法 @Target(ElementType.METHOD) // 注解仅作用于方法 @Retention(RetentionPolicy.RUNTIME) // 运行时生效(AOP需要运行时识别) public @interface LogAnnotation { // 可自定义属性,比如日志描述 String value() default ""; }
步骤 2:在目标方法上添加该注解
@Service public class DeptServiceImpl implements DeptService { // 给list方法添加自定义日志注解 @LogAnnotation("查询所有部门") @Override public List<Dept> list() { // 业务逻辑 return new ArrayList<>(); } // 给delete方法添加自定义日志注解 @LogAnnotation("删除部门") @Override public void delete(Integer id) { // 业务逻辑 } // 该方法无注解,不会被切面拦截 @Override public void save(Dept dept) { // 业务逻辑 } }
步骤 3:编写切面,用 @annotation 匹配注解方法
@Component @Aspect @Slf4j public class LogAspect { // 核心:匹配所有标注了LogAnnotation注解的方法 @Pointcut("@annotation(com.itheima.anno.LogAnnotation)") private void logPt(){} // 前置通知:拦截所有带@LogAnnotation的方法 @Before("logPt()") public void beforeLog(JoinPoint joinPoint) { // 获取注解属性(可选) MethodSignature signature = (MethodSignature) joinPoint.getSignature(); LogAnnotation annotation = signature.getMethod().getAnnotation(LogAnnotation.class); log.info("AOP日志:执行{}方法,注解描述:{}", signature.getMethodName(), annotation.value()); } }
执行效果
  • 调用list()delete()方法时,会触发beforeLog(),打印日志;
  • 调用save()方法时,因为无@LogAnnotation注解,不会触发切面;
  • 核心优势:无需关心方法的路径、参数、返回值,只要加了注解就拦截,新增需要日志的方法时,仅需加注解即可,无需修改切面代码。

三、@annotation 的进阶用法

1. 组合表达式(和 execution () 联用)

可以通过&&/||/!@annotationexecution()组合,实现更精准的匹配:

// 匹配:标注了@LogAnnotation 且 属于DeptService接口的方法 @Pointcut("@annotation(com.itheima.anno.LogAnnotation) && execution(* com.itheima.service.DeptService.*(..))") private void combinePt(){}
2. 切面中获取注解属性

在通知方法中可以获取注解的自定义属性,实现更灵活的逻辑:

@Before("logPt()") public void beforeLog(JoinPoint joinPoint) { // 1. 获取方法签名 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); // 2. 获取方法上的注解对象 LogAnnotation logAnno = method.getAnnotation(LogAnnotation.class); // 3. 获取注解属性值 String desc = logAnno.value(); // 4. 业务逻辑:比如根据注解属性区分日志类型 log.info("执行方法:{},日志描述:{}", method.getName(), desc); }
3. 匹配多个注解

通过||匹配多个注解标注的方法:

// 匹配标注了@LogAnnotation 或 @AuthAnnotation的方法 @Pointcut("@annotation(com.itheima.anno.LogAnnotation) || @annotation(com.itheima.anno.AuthAnnotation)") private void multiAnnoPt(){}

四、@annotation vs execution(核心区别)

维度@annotationexecution()
匹配依据方法上的注解标记方法的路径、返回值、参数、修饰符
灵活性高(新增拦截方法仅需加注解)低(新增方法需修改切入点表达式)
耦合度低(切面和业务方法解耦,靠注解关联)高(切面依赖方法的路径 / 参数等硬编码)
适用场景按业务场景标记(日志、权限、缓存)按方法路径 / 结构匹配(指定类 / 接口的方法)
示例拦截所有 @LogAnnotation 注解的方法拦截 DeptService 的 delete (Integer) 方法

五、Spring 内置注解的 @annotation 匹配

除了自定义注解,也可以匹配 Spring 内置注解(如 @GetMapping、@Transactional):

// 匹配所有标注了@Transactional的方法(监控事务方法执行) @Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)") private void transactionPt(){} // 匹配所有标注了@GetMapping的Controller方法(监控GET接口) @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)") private void getMappingPt(){}

六、注意事项

  1. 注解的 @Retention 必须是 RUNTIME:只有@Retention(RetentionPolicy.RUNTIME)的注解,才能在运行时被 AOP 识别,否则切入点会失效;
  2. 注解仅作用于方法@annotation只能匹配方法上的注解(注解的 @Target 需为ElementType.METHOD),无法匹配类 / 字段上的注解;
  3. 避免范围过大:如果自定义注解被大量方法标注,会导致 AOP 拦截范围过广,建议按业务模块拆分注解(如 @DeptLog、@EmpLog);
  4. 和 @within 的区别@annotation匹配 “方法上有注解” 的方法,@within匹配 “类上有注解” 的所有方法(比如类加 @Service,匹配该类所有方法)。

总结

  1. @annotation 核心作用:匹配所有标注了指定注解的方法,是 “面向注解” 的切入点定义方式;
  2. 核心优势:灵活、低耦合,新增拦截方法仅需加注解,无需修改切面;
  3. 典型场景:日志记录、权限校验、缓存控制、接口限流等按业务场景划分的拦截需求;
  4. 关键对比execution()是 “按方法结构匹配”,@annotation是 “按注解标记匹配”,生产中可根据需求选择或组合使用;
  5. 核心语法@Pointcut("@annotation(注解全限定类名)"),切面中可通过方法签名获取注解属性,实现个性化逻辑。

简单来说,@annotation让 AOP 从 “硬编码匹配方法” 变成了 “注解标记匹配方法”,是 Spring AOP 中更贴合业务开发的切入点方式。

再举一个@annotation的例子

@annotation和execution的区别是什么?

如何在@annotation中使用通配符?

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询