接上篇:https://developer.aliyun.com/article/1228286?groupCode=java
六、 公有方法代理
SpringCGLIB代理生成的代理类是一个继承被代理类,通过重写被代理类中的非final的方法实现代理。所以,SpringCGLIB代理的类不能是final类,代理的方法也不能是final方法,这是由继承机制限制的。
1. 问题现象
这里举例一个简单的例子,只有超级用户才有删除公司的权限,并且所有服务函数被AOP拦截处理异常。例子代码如下:
1) UserService.java
2) CompanyService.java
当我们调用CompanyService的deleteCompany方法时,居然也抛出空指针异常(NullPointerException),因为调用UserService类的getSuperUser方法获取的超级用户为null。但是,我们在CompanyService类的deleteCompany方法中,每次都通过UserService类的setSuperUser方法强制指定了超级用户,按道理通过UserService类的getSuperUser方法获取到的超级用户不应该为null。其实,这个问题也是由AOP代理导致的。
2. 问题分析
使用SpringCGLIB代理类时,Spring会创建一个名为“UserService
EnhancerBySpringCGLIB
EnhancerBySpringCGLIB???????? ” 的代理类。反编译这个代理类,得到以下主要代码:
可以看出,这个代理类继承了UserService类,只代理了setSuperUser方法,但是没有代理getSuperUser方法。所以,当我们调用setSuperUser方法时,设置的是原始对象实例的superUser字段值;而当我们调用getSuperUser方法时,获取的是代理对象实例的superUser字段值。如果把这两个方法的final修饰符互换,同样存在获取超级用户为null的问题。
3. 避坑方法
1) 严格遵循CGLIB代理规范,被代理的类和方法不要加final修饰符
严格遵循CGLIB代理规范,被代理的类和方法不要加final修饰符,避免动态代理操作对象实例不同(原始对象实例和代理对象实例),从而导致数据不一致或空指针问题。
2) 缩小CGLIB代理类的范围,能不用被代理的类就不要被代理
缩小CGLIB代理类的范围,能不用被代理的类就不要被代理,即可以节省内存开销,又可以提高函数调用效率。
接下篇:https://developer.aliyun.com/article/1228284?spm=a2c6h.13148508.setting.25.7be64f0ebemzoR