现象
之前管理系统为了统计用户操作的点击情况,开发了一个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