当前位置:   article > 正文

记一次 SpringBoot 项目启动失败排查 和 DubboReference 源码分析_@dubboreference(check = false)

@dubboreference(check = false)

问题现象

在我们项目中有一个公司内部的二方包,里面有一个类:MvcInterceptorAutoConfiguration ,里面定一个了一个 Bean accessContextResolver 。生成这个 Bean 需要自动注入另一个 Bean :accessContextService。代码如下:

  1. public class MvcInterceptorAutoConfiguration implements WebMvcConfigurer, ApplicationContextAware {
  2. @Bean
  3. public AccessContextResolver accessContextResolver(@Autowired AccessContextService accessContextService, @Autowired WebAuthConfig webAuthConfig) {
  4. return new DefaultAccessContextResolver(webAuthConfig, accessContextService);
  5. }
  6. }

在我们项目中又有一个类:ProxyCenter ,它里面用 @DubboReference 定义了 accessContextService 。代码如下

@
  1. Component
  2. public class ProxyCenter {
  3. @DubboReference(timeout = 10000, check = false, version = "1.0.0")
  4. private AccessContextService accessContextService;
  5. ...
  6. }

但是在项目启动过程中报如下的错

  1. ***************************
  2. APPLICATION FAILED TO START
  3. ***************************
  4. Description:
  5. 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.
  6. Action:
  7. 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 实现,所有的实现都是在子类中的。

  1. protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
  2. ...
  3. Iterator var5 = this.getBeanPostProcessors().iterator();
  4. BeanPostProcessor bp = (BeanPostProcessor)var9.next();
  5. if (bp instanceof InstantiationAwareBeanPostProcessor) {
  6. InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor)bp;
  7. pvs = ibp.postProcessPropertyValues((PropertyValues)pvs, filteredPds, bw.getWrappedInstance(), beanName);
  8. if (pvs == null) {
  9. return;
  10. }
  11. }
  12. ...
  13. }

以下是 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 这个属性的填充动作。

  1. public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
  2. //找对象
  3. InjectionMetadata metadata = this.findInjectionMetadata(beanName, bean.getClass(), pvs);
  4. try {
  5. //执行注入
  6. metadata.inject(bean, beanName, pvs);
  7. return pvs;
  8. } catch (BeanCreationExcept
本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号