现象

之前管理系统为了统计用户操作的点击情况,开发了一个aop的切面,当用户在界面上点击新增/修改等按钮的时候,可以计数,从而进行一些行为的分析,本来是一个再正常不过的需求。部署之后,发现点击按钮无反应,调用失败,同时打不开。

根本原因

刚开始觉得很奇怪,AOP切面怎么会影响页面方法打不开,经过debug,发现是里面的注入类Service为null,导致的空指针。不用AOP则没问题,进一步网上查找原因。最终定位是修改按钮对应的方法是private,在AOP适配的时候会导致service注入失败,并且同一个service在其他的public方法中没有这种情况。就是controller里面的private。

改进点

1、解决本问题:把update方法从private改成public

参考文献

1、@Autowired背后实现的原理是怎样的?

详见:@Autowired背后实现的原理是怎样的?@Autowired注解的实现原理



2、为啥aop能影响@Autowired注入失败?

详见:AOP导致Autowired注入为null:https://juejin.cn/post/7102226285216137223

定义在切面AOP下的Controller类会走代理,不管private还是public方法bean都是null值,是因为CGLIB在做初始化的时候本身是没有bean属性注入的。

当方法为private的时候,由于没有被AOP拦截,它继续使用代理类,代理类中的 bean=null。

因此我们可以判定public方法在AOP过程中有其他的操作,不然bean的属性也是null。

org.springframework.aop.framework.CglibAopProxy类中有一个静态内部类CglibMethodInvocation,其中有一个方法invokeJoinpoint()

当publicMethod=true的时候,就会用实际对象来进行反射调用,实际对象的bean属性值我们之前已经看到了,是已经注入的。

因此public方法的bean会重新赋值。

    /**
    * Gives a marginal performance improvement versus using reflection to
    * invoke the target when invoking public methods.
    */
   @Override
   protected Object invokeJoinpoint() throws Throwable {
      if (this.publicMethod) {
         return this.methodProxy.invoke(this.target, this.arguments);
      }
      else {
         return super.invokeJoinpoint();
      }
   }
}

3、Spring默认AOP代理方式是什么?

详见:为什么你写的Controller里,private方法中的bean=null?:https://blog.csdn.net/q258523454/article/details/118118553

4、AOP避坑指南

详见:https://www.liaoxuefeng.com/wiki/1252599548343744/1339039378571298