存储式测斜仪的设计与应用:从MEMS传感器到工程监测
2026/7/21 23:37:01
execution(修饰符? 返回值 包名.类名.方法名(参数类型) throws异常?)其中?表示可选,核心是返回值、包类路径、方法名、参数这四部分。pt()切入点匹配的方法执行之前,执行before()方法中的逻辑(打印日志)。//@Pointcut("execution(public void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))")com.itheima.service.impl.DeptServiceImpl类中,public修饰、返回值为void、方法名delete、参数为Integer类型的方法;//@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)方法;//@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.service、com.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.DeptService、com.itheima.service.DeptService、com.itheima.abc.service.DeptService);DeptService类 / 接口的所有方法。//@Pointcut("execution(* com..*.*(..))")com包下所有类、所有方法(返回值任意、参数任意);//@Pointcut("execution(* *(..))") //慎用慎用:会导致 AOP 通知作用于所有方法,包括 Spring 内置方法(如 Controller、Service、甚至框架自身的方法),性能极差且易出问题。@Pointcut("execution(* com.itheima.service.DeptService.list()) || " + "execution(* com.itheima.service.DeptService.delete(java.lang.Integer))") private void pt(){}||:逻辑 “或”,表示匹配任意一个表达式的方法;execution(* com.itheima.service.DeptService.list()):DeptService接口中无参数、方法名 list的方法(*表示返回值任意);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 |
||/&&/!可以灵活组合多个切入点,满足复杂匹配需求;@Pointcut定义切入点,最终仅匹配DeptService.list()和DeptService.delete(Integer)两个方法;execution()表达式的核心是 “返回值 + 包类路径 + 方法名 + 参数”,配合*/..通配符和||逻辑符可灵活匹配;execution(* *(..)))会导致性能问题,生产环境优先精准匹配;@Before("pt()")表示在匹配的方法执行前,执行before()方法的日志打印逻辑。@annotation是 Spring AOP 中另一种核心的切入点表达式语法,它的核心逻辑是:匹配所有标注了指定注解的方法,相比execution()(按方法路径 / 参数匹配),@annotation更灵活、更贴合 “面向业务场景” 的切入点定义(比如标记 “需要日志”“需要权限校验” 的方法)。
@Pointcut("@annotation(注解的全限定类名)") // 简写(若注解和切面类在同一包,可省略包名) @Pointcut("@annotation(自定义注解类名)")只要目标方法上标注了指定的注解,该方法就会被切入点匹配,AOP 通知(@Before/@After 等)就会作用于该方法。
我们通过 “给方法加日志注解,切面拦截所有带该注解的方法” 这个场景,完整演示@annotation的用法:
// 自定义注解:标记需要打印日志的方法 @Target(ElementType.METHOD) // 注解仅作用于方法 @Retention(RetentionPolicy.RUNTIME) // 运行时生效(AOP需要运行时识别) public @interface LogAnnotation { // 可自定义属性,比如日志描述 String value() default ""; }@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) { // 业务逻辑 } }@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和execution()组合,实现更精准的匹配:
// 匹配:标注了@LogAnnotation 且 属于DeptService接口的方法 @Pointcut("@annotation(com.itheima.anno.LogAnnotation) && execution(* com.itheima.service.DeptService.*(..))") private void combinePt(){}在通知方法中可以获取注解的自定义属性,实现更灵活的逻辑:
@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); }通过||匹配多个注解标注的方法:
// 匹配标注了@LogAnnotation 或 @AuthAnnotation的方法 @Pointcut("@annotation(com.itheima.anno.LogAnnotation) || @annotation(com.itheima.anno.AuthAnnotation)") private void multiAnnoPt(){}| 维度 | @annotation | execution() |
|---|---|---|
| 匹配依据 | 方法上的注解标记 | 方法的路径、返回值、参数、修饰符 |
| 灵活性 | 高(新增拦截方法仅需加注解) | 低(新增方法需修改切入点表达式) |
| 耦合度 | 低(切面和业务方法解耦,靠注解关联) | 高(切面依赖方法的路径 / 参数等硬编码) |
| 适用场景 | 按业务场景标记(日志、权限、缓存) | 按方法路径 / 结构匹配(指定类 / 接口的方法) |
| 示例 | 拦截所有 @LogAnnotation 注解的方法 | 拦截 DeptService 的 delete (Integer) 方法 |
除了自定义注解,也可以匹配 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(){}@Retention(RetentionPolicy.RUNTIME)的注解,才能在运行时被 AOP 识别,否则切入点会失效;@annotation只能匹配方法上的注解(注解的 @Target 需为ElementType.METHOD),无法匹配类 / 字段上的注解;@annotation匹配 “方法上有注解” 的方法,@within匹配 “类上有注解” 的所有方法(比如类加 @Service,匹配该类所有方法)。execution()是 “按方法结构匹配”,@annotation是 “按注解标记匹配”,生产中可根据需求选择或组合使用;@Pointcut("@annotation(注解全限定类名)"),切面中可通过方法签名获取注解属性,实现个性化逻辑。简单来说,@annotation让 AOP 从 “硬编码匹配方法” 变成了 “注解标记匹配方法”,是 Spring AOP 中更贴合业务开发的切入点方式。
再举一个@annotation的例子
@annotation和execution的区别是什么?
如何在@annotation中使用通配符?