java是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由Sun Microsystems公司于1995年5月推出的Java程序设计语言和Java平台(即JavaEE, JavaME, JavaSE)的总称。本站提供基于Java框架struts,spring,hibernate等的桌面应用、web交互及移动终端的开发技巧与资料

保持永久学习的心态,将成就一个优秀的你,来 继续搞起java知识。

最近做项目,在接入flume监控(开源的分布式监控系统)时,发现监控异常,通过排查发现是spring创建代理类DefaultAdvisorAutoProxyCreator和BeanNameAutoProxyCreator的声明顺序不对导致,下面介绍一下问题和解决过程:

1、异常描述:

1.1、异常简述:服务有10台机器,2台机器flume监控正常,8台机器flume有部分方法无法监控。

1.2、异常详情:

(1)服务接入flume监控,上线后发现有8台机器flume监控不完全,漏掉2个方法没有打印监控log。

(2)线上代码和各种配置都统一,也找了维护flume的同学看了下,没有看出flume有异常。

(3)重启服务,情况无变化,正常的2台还是正常,异常的8台还是异常。

(4)测试环境正常。

2、问题排查:

2.1、发现去掉flume监控log,只配本地log,仍然存在此异常。因此怀疑是代理的配置引起的问题。

2.2、flume的代理是DefaultAdvisorAutoProxyCreator,工程中存在另外一个BeanNameAutoProxyCreator代理,用来监控接口耗时。

2.3、这两个代理拦截了一个相同的bean:clearService,flume没有打印监控log的两个方法正好都属于这个bean。

2.4、检查10台线上机器加载这两个代理的顺序,flume正常的机器加载顺序为DefaultAdvisorAutoProxyCreator、BeanNameAutoProxyCreator,异常机器均为BeanNameAutoProxyCreator、DefaultAdvisorAutoProxyCreator。2.5、在BeanNameAutoProxyCreator声明中加入属性 depends-on=“DefaultAdvisorAutoProxyCreator”,保证DefaultAdvisorAutoProxyCreator代理先实例化。重启服务,发现DefaultAdvisorAutoProxyCreator确实优先实例化,但是flume监控依然异常。怀疑不是实例化顺序的问题。(BeanNameAutoProxyCreator的声明放在DefaultAdvisorAutoProxyCreator前面)

3、解决问题:

保证先加载DefaultAdvisorAutoProxyCreator,再加载BeanNameAutoProxyCreator

3.1、查看源码,发现DefaultAdvisorAutoProxyCreator和BeanNameAutoProxyCreator两个代理都实现了Ordered接口,order值越小执行优先级越高,于是尝试在代理配置中注入order属性。重启服务,flume监控正常。

4、原理、概念介绍:

4.1、depends-on属性:保证当前bean在所依赖的bean初始化之后才初始化。

4.2、BeanPostProcessor接口:在Spring容器完成Bean的初始化前后,添加一些自己的逻辑处理,我们就可以定义一个或者多个BeanPostProcessor接口的实现。比如当Spring容器启动时,AutowiredAnnotationBeanPostProcessor 将扫描 Spring 容器中所有 Bean,当发现 Bean 中拥有 @Autowired 注释时就找到和其匹配(默认按类型匹配)的Bean,并注入到对应的地方中去。

4.3、DefaultAdvisorAutoProxyCreator类:

(1)自动创建代理的类,它会 cahce 容器中所有注册的 advisor, 然后搜索容器中所有的 bean , 如果某个 bean 满足 advisor 中的 Pointcut, 那么将会被自动代理。

(2)实现了BeanPostProcessor和Ordered接口。

(3)它会根据bean的类型判断该bean是否匹配。

4.4、BeanNameAutoProxyCreator类:(1)自动创建代理的类,它会根据拦截器和设置的Bean的名称表达式做匹配来创建代理。

(2)实现了BeanPostProcessor和Ordered接口。

4.5、order属性:(1)PriorityOrdered接口继承自Ordered接口,且实现了PriorityOrdered接口的类优先级更高。若2个对象都是PriorityOrdered或Ordered接口的实现类,那么比较Ordered接口的getOrder方法得到order值,值越低,优先级越高。

(2)在DefaultAdvisorAutoProxyCreator和BeanNameAutoProxyCreator的场景中,order的作用是决定谁先被注册到ConfigurableListableBeanFactory中,先注册的BeanPostProcessor会优先对bean进行处理。如果不配置order属性,会按照bean的声明顺序注册bean。(注意:区别于bean实例化的顺序,先实例化不一定先注册到ConfigurableListableBeanFactory)

5、实例化bean流程:

5.1、创建代理流程:

5.2、如5.1的代码,循环beanPostProcessor,调用后置方法。对于DefaultAdvisorAutoProxyCreator和BeanNameAutoProxyCreator这两个beanPostProcessor,如果满足条件创建了代理,第7行返回新创建的代理对象;如果不满足创建代理条件,第7行返回传入的result对象参数。

6、场景解析:

6.1、

(1)条件:BeanNameAutoProxyCreator优先注册到beanFactory、BeanNameAutoProxyCreator和DefaultAdvisorAutoProxyCreator拦截公共的bean:clearService、当前bean为com.meituan.pay.clear.service.impl.ClearServiceImpl。

(2)分析:对于5.1中的6-11行代码:

步骤一:BeanNameAutoProxyCreator优先执行,第7行代码入参为(com.meituan.pay.clear.service.impl.ClearServiceImpl,"clearService"),满足4.4中代码块beanNames匹配条件,创建了代理,并返回代理对象(com.sun.proxy.$Proxy17)赋值给result。

步骤二:接着DefaultAdvisorAutoProxyCreator执行,从4.3可知,DefaultAdvisorAutoProxyCreator是根据bean的类型判断是否匹配,这时postProcessAfterInitialization入参为步骤一返回的代理对象和初始bean的名字,即(com.sun.proxy.$Proxy17,"clearService"),代理对象的名字虽然是clearService,但bean的类型与4.3中代码块patterns的配置不匹配,所以无法为clearService创建代理。

6.2、

(1)条件:DefaultAdvisorAutoProxyCreator优先注册到beanFactory、BeanNameAutoProxyCreator和BeanNameAutoProxyCreator拦截公共的bean:clearService、当前bean为com.meituan.pay.clear.service.impl.ClearServiceImpl。

(2)分析:对于5.1中的6-11行代码:

步骤一:DefaultAdvisorAutoProxyCreator优先执行,第7行代码入参为(com.meituan.pay.clear.service.impl.ClearServiceImpl,"clearService"),满足4.3中代码块patterns匹配条件,创建了代理,并返回代理对象赋值给result。

步骤二:接着BeanNameAutoProxyCreator执行,从5.1.4可知,BeanNameAutoProxyCreator是根据bean的名字判断是否匹配,这时postProcessAfterInitialization入参为步骤一返回的代理对象和初始bean的名字,即(com.sun.proxy.$Proxy17,"clearService"),虽然bean变了,但是bean的名字没变,满足4.4中代码块beanNames匹配条件,创建代理。

7、Tips:

7.1、mvn dependency:sources命令:开始走了一些弯路,直接看了class反编译文件- -#,发现一些代码逻辑错误后才去搞了源码。用mvn dependency:sources命令可以下载依赖包的源码。

8、Q&A:

8.1、

question:对象regexpMethodPointcutAdvisor的pattern属性如果使用接口名字而非实现类名字,是否就没有这个问题?

answer:patterns使用接口名字可以解决此问题。现在是jdk动态代理方式,改成cglib方式也没问题。

springBeanNameAutoProxyCreDefaultAdvisorAutoPr代理类异常

因为水平有限,难免有疏忽或者不准确的地方,希望大家能够直接指出来,我会及时改正。一切为了知识的分享。

后续会有更多的精彩的内容分享给大家。