Spring AOP 可是拦不下接口方法上的注解

背景

最近在做一个小的工程项目,通过使用AOP和自定义注解的方式实现了操作日志的记录,并单独封装以供其他模块的调用。

项目模块结构简示

如上所示 itf.service 定义 api 接口规范,然后 controller实现所接口并完善方法;所以最初为了代码上的好看,将自定义的注解直接标注在了 itf.service 层的方法上。然而实际操作发现,这样的注解并不会被扫描到,并未发挥日志记录的作用。

这是为什么呢?

首先在说之前,我们先聊一下注解的继承问题。

注解的继承问题

对于接口,在接口中的注解无论如何都不能被继承,不论是子接口继承父接口的情况还是接口的实现类的情况,不论是对接口上还是接口中的方法上的注解,都不能被继承。以上经过测试所得。

在说注解继承时,我们先说下注解里一个叫 @Inherited 的元注解。

如果一个类用上了 @Inherited 修饰的注解,那么其子类也会继承这个注解,与方法上的注解的继承性无关。

需要注意的是:

  • 接口用上个 @Inherited 修饰的注解,其实现类不会继承这个注解

  • 父类的方法用了 @Inherited 修饰的注解,子类也不会继承这个注解

对于第二条为什么呢?其实当我们把注解用在方法中,就没有没有所谓继承问题,只有方法的重写问题

即对于类中方法上的注解,若子类重写了父类带注解方法,从子类无法获取到注解。

子类没有重写父类带注解方法,可以通过子类获得注解。这个与注解是否被标注 @Inherited 无关。

那为啥注解在接口上没作用?

因为我们使用了 AOP 特性,与之相关联的便是 Spring 动态代理 了。Spring 的动态代理主要分为两种,一种是JDK 动态代理 ;一种是CGLIB 动态代理

  1. 使用 JDK 动态代理

    JDK 动态代理主要是针对实现了某个接口的类。该方式基于反射的机制实现,会生成一个实现相同接口的代理类,然后通过对方法的充写,实现对代码的增强。

    在该方式中接口中的注解无法被实现类继承,AOP 中的切点无法匹配上实现类,所以也就不会为实现类创建代理,所以我们使用的类其实是未被代理的原始类,自然也就不会被增强了。

  2. 使用 CGLIB 动态代理

    在不存在切点注解继承的情况,AOP 可进行有效拦截(CGLIB动态代理)。但是还要考虑以下存在注解继承的情况:

    有父类 Parent,和子类 Sub ,切点注解在父类方法。则根据上边提到的只有方法的重写问题,可知,被重写的方法将不会被拦截,而未重写的方法则走 Parent 路线,可以被 AOP 感知拦截。

最后附上我的微信公众号: 做棵大树。厚着脸皮引个流,平时会写一些自己日常的学习心得也有一些人间烟(沙)火(雕)事哦~
扫码关注,一起进步!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
使用Spring AOP可以实现方法执行成功后改变注解的值。首先,需要在Spring配置文件中启用AOP的支持。然后,在目标方法所在的类上添加相关的注解,用于标识需要进行切面处理的方法。接下来,创建一个切面类,在切面类中定义一个环绕通知方法,该方法会在目标方法执行前后进行调用。在该环绕通知方法中,可以通过反射机制获取目标方法上的注解信息,并进行相应的处理。 假设有一个需要改变注解值的目标方法如下: ```java @MyAnnotation("old value") public void myMethod() { // 目标方法的实现逻辑 } ``` 首先,需要创建一个自定义的注解类`MyAnnotation`: ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MyAnnotation { String value() default ""; } ``` 然后,创建一个切面类`MyAspect`,并在该类中定义一个环绕通知方法`changeAnnotationValue`: ```java @Component @Aspect public class MyAspect { @Around("@annotation(myAnnotation)") public Object changeAnnotationValue(ProceedingJoinPoint joinPoint, MyAnnotation myAnnotation) throws Throwable { // 获取目标方法 MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method targetMethod = methodSignature.getMethod(); // 获取目标方法上的注解 MyAnnotation annotation = targetMethod.getAnnotation(MyAnnotation.class); // 修改注解值 MyAnnotation modifiedAnnotation = new MyAnnotationImpl(annotation.value() + " modified value"); // 反射机制修改注解值 MutableMethodMetadata mutableMetadata = new MutableMethodMetadata(targetMethod, false); mutableMetadata.addAnnotation(modifiedAnnotation); AnnotationUtils.synthesizeAndMergeAnnotations(modifiedAnnotation, mutableMetadata); // 调用目标方法 Object result = joinPoint.proceed(); return result; } } ``` 最后,在Spring配置文件中进行AOP的配置: ```xml <aop:aspectj-autoproxy /> <context:annotation-config /> <context:component-scan base-package="com.example" /> ``` 这样,当目标方法`myMethod`被调用时,首先会执行切面类`MyAspect`中的环绕通知方法`changeAnnotationValue`,在该方法中修改了目标方法上`MyAnnotation`注解的值,然后再执行目标方法,实现了方法执行成功后改变注解的值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值