环境:Spring6.1.7
为了提高代码的复用性和维护性,我们设计了一个通用的抽象的切面父类。这个父类封装了通用的切面逻辑,如日志记录、性能监控、异常处理等。通过继承这个抽象父类,具体的业务切面可以轻松地扩展或定制这些通用功能,而无需从零开始编写切面逻辑。如下示例:
public abstract class CommonAspect { @Pointcut("execution(public * com.pack..*.*(..))") private void commonPointcut() {} @Before("bean(*Service)") protected void beforeLog(JoinPoint jp) { System.out.println("通用日志记录...") ; } @Around("@annotation(monitor)") protected Object monitorRun(ProceedingJoinPoint pjp, Monitor monitor) throws Throwable { Object ret = null ; // TODO ret = pjp.proceed() ; // TODO return ret ; } // other}
@Component@Aspectpublic class LogAspect extends CommonAspect { @Override public void beforeLog(JoinPoint jp) { System.out.println("重写日志记录功能") ; }}
当项目运行时出现诡异的问题,既然打印了2次日志信息。
// 测试代码AnnotationConfigApplicationContext context = ... ;PersonService ps = context.getBean(PersonService.class) ;ps.queryById(1L) ;
重写日志记录功能重写日志记录功能查询Person对象
虽然执行了我们重写的方法,但是日志确输出了2遍。
容器在启动初始化解析@Aspect切面时,在获取切面类中的所有方法时,会得到两个方法(父类及子类重写的)
图片
在这里的getAdvisorMethods方法返回了3个方法,其中2个是父类中的一个是子类重写的方法
图片
那么接下来根据这2个方法就会生成对应的Advisor对象。
图片
这也就是为什么重复的原因了。Spring并没有判断我当前的这个通知是否是重写父类的方法。
注:在Spring中@Aspect定义的切面最终都会转换为Advisor对象,当代理类在执行时会遍历所有符合添加的Advisor然后从中取出对应的Advice(MethodInterceptor)对象。
既然知道了问题出现的原因,接下来就进行解决该问题。
我们可以在子类重写的方法上再加上通知类型,将切入点设置的不匹配任何方法即可
@Before("execution(public * xxxooo())")@Overridepublic void beforeLog(JoinPoint jp) { System.out.println("重写日志记录功能") ;}
在这里重写的方法上,将切入点重新写,该切入点不会匹配任何的方法,这样修改以后再次执行
重写日志记录功能查询Person对象
正常执行,没有重复输出日志。
该方法需要将Spring版本升级到6.1.8,在该版本中解决了该问题。来看看在该版本中是如何解决的。
核心方法还是在上面debug是看到的getAdvisors方法中
public class ReflectiveAspectJAdvisorFactory { public List<Advisor> getAdvisors(...) { List<Advisor> advisors = new ArrayList<>(); for (Method method : getAdvisorMethods(aspectClass)) { // 在这里加入了判断(当前方法是否与当前切面中的方法相同) // 当遍历到这里的Method是父类中的方法时,这里的getMostSpecificMethod // 方法就会判断根据方法的名称再从当前切面类中获取方法(那当然就不同了) if (method.equals(ClassUtils.getMostSpecificMethod(method, aspectClass))) { Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName); if (advisor != null) { advisors.add(advisor); } } } }}
图片
在6.1.8版本中通过这种方式就排除了父类的方法。
图片
低于6.1.8版本,都没有相应的判断。
本文链接://www.dmpip.com//www.dmpip.com/showinfo-26-93092-0.html严重!Spring AOP Bug导致切面重复执行
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com
上一篇: PHP 服务实现性能剖析、跟踪和可观察性14444444444444】=102102102102102102102102102102102102102102102102实践
下一篇: 多线程编程在 C# 中的基础概念与实现