赞
踩
在我们项目中有一个公司内部的二方包,里面有一个类:MvcInterceptorAutoConfiguration ,里面定一个了一个 Bean accessContextResolver 。生成这个 Bean 需要自动注入另一个 Bean :accessContextService。代码如下:
- public class MvcInterceptorAutoConfiguration implements WebMvcConfigurer, ApplicationContextAware {
-
- @Bean
- public AccessContextResolver accessContextResolver(@Autowired AccessContextService accessContextService, @Autowired WebAuthConfig webAuthConfig) {
- return new DefaultAccessContextResolver(webAuthConfig, accessContextService);
- }
- }
在我们项目中又有一个类:ProxyCenter ,它里面用 @DubboReference 定义了 accessContextService 。代码如下
@
- Component
- public class ProxyCenter {
-
- @DubboReference(timeout = 10000, check = false, version = "1.0.0")
- private AccessContextService accessContextService;
-
- ...
- }
但是在项目启动过程中报如下的错
- ***************************
- APPLICATION FAILED TO START
- ***************************
-
- Description:
-
- Parameter 0 of method accessContextResolver in cn.xxx.xxx.xxx.xxx.config.MvcInterceptorAutoConfiguration required a bean of type 'cn.xxx.xxx.xxx.xxx.service.AccessContextService' that could not be found.
-
-
- Action:
-
- Consider defining a bean of type 'cn.xxx.xxx.xxx.xxx.service.AccessContextService' in your configuration.
这个错误可能大家都很熟悉了,意思是 Spring 在创建 accessContextResolver 这个 Bean 的时候需要自动注入 accessContextService 这个 Bean ,但是 Spring 容器找不到这个 Bean ,所以启动失败。
Dubbo版本:2.7.0
对于这个问题本质是 @Autowired 不能注入 @DubboReference 声明过的 Bean ,最主要需要弄清楚 @DubboReference 和 @Autowired 所做的事情,并且分别都是在什么时候做的。
如果只使用 @Autowired 的时候,并不会出现以上这种情况,所以我们定位问题的方向优先去看 @DubboReference 的实现逻辑。
先讲一个背景知识:我们知道 Spring 创建一个 Bean 大致需要经过实例化对象、属性填充、初始化对象这几步,其中属性填充是在 populateBean 这个方法中实现的(代码如下),这里有一段逻辑是,获取 Bean 工厂中所有的 BeanPostProcessor ,如果是 InstantiationAwareBeanPostProcessor 类型,那么就调用 postProcessPropertyValues 方法。
注意:InstantiationAwareBeanPostProcessor 是一个抽象类,它本身没有提供 postProcessPropertyValues 实现,所有的实现都是在子类中的。
- protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
-
- ...
-
- Iterator var5 = this.getBeanPostProcessors().iterator();
-
- BeanPostProcessor bp = (BeanPostProcessor)var9.next();
- if (bp instanceof InstantiationAwareBeanPostProcessor) {
- InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor)bp;
- pvs = ibp.postProcessPropertyValues((PropertyValues)pvs, filteredPds, bw.getWrappedInstance(), beanName);
- if (pvs == null) {
- return;
- }
- }
-
- ...
- }
以下是 InstantiationAwareBeanPostProcessorAdapter 实现类, 这里只列举了和我们这次问题相关的子类
InstantiationAwareBeanPostProcessorAdapter
|
AutowiredAnnotationBeanPostProcessor ( Spring 提供属性/方法注入实现)
|
AbstractAnnotationBeanPostProcessor 【com.alibaba.spring......】
|
ReferenceAnnotationBeanPostProcessor 【org.apache.dubbo......】( Dubbo提供的 @DubboReference, @Reference 实现)
从上面的源码和类的继承关系我们可以得出结论:spring进行属性填充的时候,会调用 ReferenceAnnotationBeanPostProcessor 这个类的 postProcessPropertyValues 方法。而 ReferenceAnnotationBeanPostProcessor 这个类就是Dubbo提供的 Bean 的后置处理器, @DubboReference, @Reference 就是在这个方法里面实现的。
在了解了上面的背景知识后,我们就开始进入 @DubboReference 的源码分析。下面列出来的是 ReferenceAnnotationBeanPostProcessor 对于 postProcessPropertyValues 的实现。
我们要注意一点,那就是此时正在创建的 Bean 是 proxyCenter,至于为什么是 proxyCenter 这个 Bean ,这个很简单,因为在本案例中 accessContextService 是 ProxyCenter 这个类的属性,所以在创建 proxyCenter 这个 Bean 的时候发生对 accessContextService 这个属性的填充动作。
- public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
-
- //找对象
- InjectionMetadata metadata = this.findInjectionMetadata(beanName, bean.getClass(), pvs);
-
- try {
- //执行注入
- metadata.inject(bean, beanName, pvs);
- return pvs;
- } catch (BeanCreationExcept
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。