切面编程遇到的注入service为null的问题分析
最近学习并运用了AOP切面编程,主要是在所有的controller方法中增加权限校验、时间统计。步骤如下:
1、引入依赖包
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/8513ee80d0646eee9001a9aa28c17001.png)
2、增加AOP配置类,并定义切入点@PointCut以及切入后要做的事情。
3、由于并不是controller中所有的api接口,需要进行权限校验,所以,我增加了一个自定义注解@CheckAuth,对controller下配有该注解的方法才进行权限校验。
3、增加了一个接口,进行了测试。
/testAop接口可以正常运行,以为就完成了。结果出现了问题,某些api接口中,通过@Autowired引入的service对象,出现了null的情况,导致接口异常。
原来,这个与Springboot的动态代理有关。Springboot默认cglib动态代理模式。动态代理是什么?Cglib又是怎么代理的?AOP切面编程跟cglib又有什么关联?
通俗地讲,代理proxy就像是代理人,代表他人办理事务,他人就是被代理人。如果被代理人固定为一个,就是静态代理,反之,就是动态代理。动态代理分为JDK动态代理和CGLIB动态代理。
JDK动态代理,是jre内部提供的,可以直接使用,最关键的两个词儿就是proxy、invoke,就是Proxy创建一个对象实例,实现InvocationHandler的invoke方法,同时InvocationHandler包含了被代理对象,就像是被代理对象的一个包装。如下图,Proxy代理了InvocationHandler,InvocationHandler代理了Target,两级代理,也是对象之间的代理。开发人员可在InvocationHandler的invoke()方法内部,写自己的代码,实现某些操作,即代码增强。
Ciglib动态代理,本质是使用了继承,并且子类重写父类的方法(方法名与父类方法一致)。最关键的词儿就是MethodInterceptor、intercept。B(代理类)继承了Target类、实现了MethodInterceptor,且子类B中有重写方法、cglib方法(名叫“CGLIB”+“ 父 类 方 法 名 父类方法名 父类方法名”的方法)、父类方法(不可见),还有一个统一的拦截方法(增强方法intercept)。其中重写方法和cglib方法是有映射关系的。Ciglib代理,就是通过这个intercept,去调用了ciglib方法,继而去调用代理类中的重写方法(因为invoke反射机制无法直接调用父类的方法)。我们可以在intercept方法中写增强代码。
AOP切面编程,默认就是用了ciglib的动态代理。其中代理类中的ciglib方法只能构建父类的非private方法,controller中的private对象和方法,是无法被动态代理继承的。所以AOP切入的controller中接口是必须要声明为public的。