
Dubbo无法处理Spring代理对象
背景
在阿里重启开源之前,基于dubbo注解的服务暴露有很多缺陷。公司小伙伴找到我帮他分析一个dubbo服务使用aop不生效的问题,最后分析发现是在使用aop场景下如果接口包含循环依赖,dubbo服务暴露是拿不到代理,所以就导致不生效了。
为了简化背景,业务方同学对外暴露controller接收http请求, 依赖关系:
1 | Controller |
这里大家应该发现循环依赖了, DeliveryOperateService是应用了aop拦截,这种场景在开源版本dubbo 2.5.8之前是无法正确处理的。
1 | // Controller |
在正常情况下,如果使用aop在dubbo暴露服务时会传递正确的spring动态代理后的对象:

Dubbo服务暴露实际持有的对象是代理后的对象。但是因为循环依赖Dubbo提前拿到DeliveryOperateService非代理的实例。
我们来看下为什么这种场景Dubbo无法处理?

Step1, Spring启动初始化Controller, 对属性进行注入
Step2, Controller触发DeliveryOperateService创建实例

第一次依赖注入就会触发bean的实例化并且保存在exposedObject中,,注意,这里是非代理对象。
Step3, DeliveryOperateService依赖CancelDistOrderProcessor并触发它初始化

这里也没什么特殊的,在populateBean会触发循环依赖DeliveryOperateService加载,这时候earlySingletonExposure值为true, 代表bean提前暴露。
Step4, 在前一步触发,DeliveryOperateService其实会创建动态代理

循环引用会导致提前暴露earlySingletonExposure=true,这个时候加载的是getEarlyBeanReference,在里面创建spring动态代理:

在创建完动态代理后,DeliveryOperateService会加入earlyProxyReferences,后面再获取这个bean就不会再重复创建代理了。

到此,DeliveryOperateService确实会创建,并且会用在CancelDistOrderProcessor对应注入的字段中。
Step6, 触发Dubbo服务暴露的实例不是代理
因为在CancelDistOrderProcessor中已经触发了代理生成,所以第Step1中的实例不会再创建代理了。

在代码555会触发dubbo AnnotationBean进行服务暴露,但是这个不是代理实例了,但是为什么spring还是正确返回代理后的实例呢?

因为循环引用触发earlySingletonExposure=true, 并且在前面已经生成过动态代理了,可以直接在getSingleton拿到动态代理的返回了。
好了,基本原因已经分析的够清楚了,我觉的有2点注意事项:
dubbo原来注解实现声明周期没搞清楚spring如果能提前判断循环引用获取exposedObject也没问题
1 | if (earlySingletonExposure) { |
比如把循环引用逻辑提前到populateBean之前判断一下。
为什么在开源dubbo 2.5.8版本之后没有这个问题?
重写后的注解实现,我深入研究过,新版本实现不会提前使用没有代理的bean,关键代码:
1 | // ServiceAnnotationBeanPostProcessor |
ServiceAnnotationBeanPostProcessor 实现BeanDefinitionRegistryPostProcessor接口,会在所有spring bean真正初始化前完成dubbo服务的注册,整个生命周期中不会触碰到代理前的对象。
总结
我其实平时不太习惯写文章,但是发现分析后的问题记录下来可以让更多同学收益。