记一次 SpringBoot 项目启动失败排查 和 DubboReference 源码分析

问题现象

在我们项目中有一个公司内部的二方包,里面有一个类:
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 的实现逻辑。

@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 {
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值