spring使用dubbo-api与dubbo-xml混合配置启动服务失败

川长思鸟来 2024-01-09 06:13 106阅读 0赞

spring使用dubbo-api与dubbo-xml混合配置启动服务失败

问题描述

springboot+springmvc+dubbo服务启动时失败,配置采用的dubbo的@EnableDubbo(multipleConfig = true)模式。断点跟入抛出异常的点发现确实applicationConfig注入的实例是非空的,但属性全部为空。配置均是按照约定配置
dubbo配置

  1. dubbo.applications.my-application.logger=slf4j
  2. dubbo.protocols.protocol.accesslog=true
  3. dubbo.protocols.protocol.id=dubbo
  4. dubbo.protocols.protocol.name=dubbo
  5. dubbo.providers.provider.filter=-exception,businessException,default
  6. dubbo.applications.my-application.id=${
  7. project.name}
  8. dubbo.applications.my-application.name=${
  9. project.name}
  10. dubbo.applications.my-application.qosEnable=${
  11. qos.enable:false}
  12. dubbo.protocols.protocol.port=${
  13. dubbo.protocol.port}
  14. dubbo.applications.my-application.qosPort=${
  15. qos.port}
  16. dubbo.protocols.protocol.threadpool=wirelessThreadPool
  17. dubbo.registries.unitRegistry.id=unitRegistry
  18. dubbo.registries.unitRegistry.address=zookeeper://...
  19. dubbo.registries.baseRegistry.id=baseRegistry
  20. dubbo.registries.baseRegistry.address=zookeeper://...
  21. dubbo.registries.commonRegistry.id=commonRegistry
  22. dubbo.registries.commonRegistry.address=zookeeper://...

异常堆栈

  1. Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
  2. 2019-05-21 17:07:45,903 ERROR [restartedMain] org.springframework.boot.SpringApplication:reportFailure:771 Application startup failed
  3. org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'errorLogMonitorProvider' defined in class path resource [com/.../wireless/log4j2/spring/boot/autoconfigure/Log4J2AutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.....wireless.monitor.provider.ErrorLogMonitorProvider]: Factory method 'errorLogMonitorProvider' threw exception; nested exception is java.lang.IllegalStateException: ApplicationConfig.application == null
  4. at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:599) ~[spring-beans-4.3.20.RELEASE.jar:4.3.20.RELEASE]
  5. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1178) ~[spring-beans-4.3.20.RELEASE.jar:4.3.20.RELEASE]
  6. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1072) ~[spring-beans-4.3.20.RELEASE.jar:4.3.20.RELEASE]
  7. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:511) ~[spring-beans-4.3.20.RELEASE.jar:4.3.20.RELEASE]
  8. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:481) ~[spring-beans-4.3.20.RELEASE.jar:4.3.20.RELEASE]
  9. at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312) ~[spring-beans-4.3.20.RELEASE.jar:4.3.20.RELEASE]
  10. at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.20.RELEASE.jar:4.3.20.RELEASE]
  11. at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308) ~[spring-beans-4.3.20.RELEASE.jar:4.3.20.RELEASE]
  12. at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.20.RELEASE.jar:4.3.20.RELEASE]
  13. at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761) ~[spring-beans-4.3.20.RELEASE.jar:4.3.20.RELEASE]
  14. at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867) ~[spring-context-4.3.20.RELEASE.jar:4.3.20.RELEASE]
  15. at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543) ~[spring-context-4.3.20.RELEASE.jar:4.3.20.RELEASE]
  16. at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) ~[spring-boot-1.5.17.RELEASE.jar:1.5.17.RELEASE]
  17. at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693) [spring-boot-1.5.17.RELEASE.jar:1.5.17.RELEASE]
  18. at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360) [spring-boot-1.5.17.RELEASE.jar:1.5.17.RELEASE]
  19. at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) [spring-boot-1.5.17.RELEASE.jar:1.5.17.RELEASE]
  20. at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118) [spring-boot-1.5.17.RELEASE.jar:1.5.17.RELEASE]
  21. at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107) [spring-boot-1.5.17.RELEASE.jar:1.5.17.RELEASE]
  22. at com.....dispatch.grafana.service.Application.main(Application.java:15) [classes/:?]
  23. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_131]
  24. at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_131]
  25. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_131]
  26. at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_131]
  27. at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-1.5.17.RELEASE.jar:1.5.17.RELEASE]
  28. Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.....wireless.monitor.provider.ErrorLogMonitorProvider]: Factory method 'errorLogMonitorProvider' threw exception; nested exception is java.lang.IllegalStateException: ApplicationConfig.application == null
  29. at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189) ~[spring-beans-4.3.20.RELEASE.jar:4.3.20.RELEASE]
  30. at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588) ~[spring-beans-4.3.20.RELEASE.jar:4.3.20.RELEASE]
  31. ... 23 more
  32. Caused by: java.lang.IllegalStateException: ApplicationConfig.application == null
  33. at com.alibaba.dubbo.config.AbstractConfig.appendParameters(AbstractConfig.java:237) ~[dubbo-2.6.5-wireless-1.jar:2.6.5-wireless-1]
  34. at com.alibaba.dubbo.config.AbstractConfig.appendParameters(AbstractConfig.java:172) ~[dubbo-2.6.5-wireless-1.jar:2.6.5-wireless-1]
  35. at com.alibaba.dubbo.config.ReferenceConfig.init(ReferenceConfig.java:303) ~[dubbo-2.6.5-wireless-1.jar:2.6.5-wireless-1]
  36. at com.alibaba.dubbo.config.ReferenceConfig.get(ReferenceConfig.java:163) ~[dubbo-2.6.5-wireless-1.jar:2.6.5-wireless-1]
  37. at com.....wireless.log4j2.spring.boot.autoconfigure.Log4J2AutoConfiguration.errorLogMonitorProvider(Log4J2AutoConfiguration.java:28) ~[log4j2-spring-boot-starter-1.0.0-20190326.032241-16.jar:1.0.0-SNAPSHOT]
  38. at com.....wireless.log4j2.spring.boot.autoconfigure.Log4J2AutoConfiguration$$EnhancerBySpringCGLIB$$46137073.CGLIB$errorLogMonitorProvider$0(<generated>) ~[log4j2-spring-boot-starter-1.0.0-20190326.032241-16.jar:1.0.0-SNAPSHOT]
  39. at com.....wireless.log4j2.spring.boot.autoconfigure.Log4J2AutoConfiguration$$EnhancerBySpringCGLIB$$46137073$$FastClassBySpringCGLIB$$7956c093.invoke(<generated>) ~[log4j2-spring-boot-starter-1.0.0-20190326.032241-16.jar:1.0.0-SNAPSHOT]
  40. at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228) ~[spring-core-4.3.20.RELEASE.jar:4.3.20.RELEASE]
  41. at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:358) ~[spring-context-4.3.20.RELEASE.jar:4.3.20.RELEASE]
  42. at com.....wireless.log4j2.spring.boot.autoconfigure.Log4J2AutoConfiguration$$EnhancerBySpringCGLIB$$46137073.errorLogMonitorProvider(<generated>) ~[log4j2-spring-boot-starter-1.0.0-20190326.032241-16.jar:1.0.0-SNAPSHOT]
  43. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_131]
  44. at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_131]
  45. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_131]
  46. at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_131]
  47. at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162) ~[spring-beans-4.3.20.RELEASE.jar:4.3.20.RELEASE]
  48. at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588) ~[spring-beans-4.3.20.RELEASE.jar:4.3.20.RELEASE]
  49. ... 23 more
  50. Caused by: java.lang.IllegalStateException: ApplicationConfig.application == null
  51. at com.alibaba.dubbo.config.AbstractConfig.appendParameters(AbstractConfig.java:222) ~[dubbo-2.6.5-wireless-1.jar:2.6.5-wireless-1]
  52. at com.alibaba.dubbo.config.AbstractConfig.appendParameters(AbstractConfig.java:172) ~[dubbo-2.6.5-wireless-1.jar:2.6.5-wireless-1]
  53. at com.alibaba.dubbo.config.ReferenceConfig.init(ReferenceConfig.java:303) ~[dubbo-2.6.5-wireless-1.jar:2.6.5-wireless-1]
  54. at com.alibaba.dubbo.config.ReferenceConfig.get(ReferenceConfig.java:163) ~[dubbo-2.6.5-wireless-1.jar:2.6.5-wireless-1]
  55. at com.....wireless.log4j2.spring.boot.autoconfigure.Log4J2AutoConfiguration.errorLogMonitorProvider(Log4J2AutoConfiguration.java:28) ~[log4j2-spring-boot-starter-1.0.0-20190326.032241-16.jar:1.0.0-SNAPSHOT]
  56. at com.....wireless.log4j2.spring.boot.autoconfigure.Log4J2AutoConfiguration$$EnhancerBySpringCGLIB$$46137073.CGLIB$errorLogMonitorProvider$0(<generated>) ~[log4j2-spring-boot-starter-1.0.0-20190326.032241-16.jar:1.0.0-SNAPSHOT]
  57. at com.....wireless.log4j2.spring.boot.autoconfigure.Log4J2AutoConfiguration$$EnhancerBySpringCGLIB$$46137073$$FastClassBySpringCGLIB$$7956c093.invoke(<generated>) ~[log4j2-spring-boot-starter-1.0.0-20190326.032241-16.jar:1.0.0-SNAPSHOT]
  58. at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228) ~[spring-core-4.3.20.RELEASE.jar:4.3.20.RELEASE]
  59. at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:358) ~[spring-context-4.3.20.RELEASE.jar:4.3.20.RELEASE]
  60. at com.....wireless.log4j2.spring.boot.autoconfigure.Log4J2AutoConfiguration$$EnhancerBySpringCGLIB$$46137073.errorLogMonitorProvider(<generated>) ~[log4j2-spring-boot-starter-1.0.0-20190326.032241-16.jar:1.0.0-SNAPSHOT]
  61. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_131]
  62. at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_131]
  63. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_131]
  64. at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_131]
  65. at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162) ~[spring-beans-4.3.20.RELEASE.jar:4.3.20.RELEASE]
  66. at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588) ~[spring-beans-4.3.20.RELEASE.jar:4.3.20.RELEASE]
  67. ... 23 more
  68. 2019-05-21 17:07:45,917 restartedMain ERROR An exception occurred processing Appender DubboSpring java.lang.IllegalStateException: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@7def2f51 has not been refreshed yet
  69. at org.springframework.context.support.AbstractApplicationContext.assertBeanFactoryActive(AbstractApplicationContext.java:1067)
  70. at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1085)
  71. at com.....wireless.fundamental.util.SpringUtils.getBean(SpringUtils.java:30)
  72. at com.....wireless.log4j2.spring.boot.autoconfigure.appender.DubboSpringAppender.doAppend(DubboSpringAppender.java:51)
  73. at com.....wireless.log4j2.spring.boot.autoconfigure.appender.DubboSpringAppender.append(DubboSpringAppender.java:43)
  74. at org.apache.logging.log4j.core.config.AppenderControl.tryCallAppender(AppenderControl.java:156)
  75. at org.apache.logging.log4j.core.config.AppenderControl.callAppender0(AppenderControl.java:129)
  76. at org.apache.logging.log4j.core.config.AppenderControl.callAppenderPreventRecursion(AppenderControl.java:120)
  77. at org.apache.logging.log4j.core.config.AppenderControl.callAppender(AppenderControl.java:84)
  78. at org.apache.logging.log4j.core.config.LoggerConfig.callAppenders(LoggerConfig.java:448)
  79. at org.apache.logging.log4j.core.config.LoggerConfig.processLogEvent(LoggerConfig.java:433)
  80. at org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:417)
  81. at org.apache.logging.log4j.core.config.LoggerConfig.log(LoggerConfig.java:403)
  82. at org.apache.logging.log4j.core.config.AwaitCompletionReliabilityStrategy.log(AwaitCompletionReliabilityStrategy.java:63)
  83. at org.apache.logging.log4j.core.Logger.logMessage(Logger.java:146)
  84. at org.apache.logging.slf4j.Log4jLogger.log(Log4jLogger.java:376)
  85. at org.apache.commons.logging.impl.SLF4JLocationAwareLog.error(SLF4JLocationAwareLog.java:216)
  86. at org.springframework.boot.SpringApplication.reportFailure(SpringApplication.java:771)
  87. at org.springframework.boot.SpringApplication.handleRunFailure(SpringApplication.java:748)
  88. at org.springframework.boot.SpringApplication.run(SpringApplication.java:314)
  89. at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118)
  90. at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107)
  91. at com.....dispatch.grafana.service.Application.main(Application.java:15)
  92. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  93. at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  94. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  95. at java.lang.reflect.Method.invoke(Method.java:498)
  96. at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)

异常抛出代码

  1. @Bean
  2. public ErrorLogMonitorProvider errorLogMonitorProvider(ApplicationConfig application, @Qualifier("baseRegistry") RegistryConfig baseRegistry) {
  3. ReferenceConfig<ErrorLogMonitorProvider> reference = new ReferenceConfig();
  4. reference.setApplication(application);
  5. reference.setRegistry(baseRegistry);
  6. reference.setInterface(ErrorLogMonitorProvider.class);
  7. reference.setVersion("1.0.0");
  8. reference.setAsync(true);
  9. reference.setCheck(false);
  10. return (ErrorLogMonitorProvider)reference.get();
  11. }

问题推测1

由于之前服务都是正常启动的,只有在增加了一个消费者之后才出现了该现象,一直没有想是这个原因,但是细想只有这个点了,于是删除引入的reference引用,果然问题不存在了,一切注入正常,服务正常。怎么会有这类问题?因为断点看到ApplicationConfig注入的是属性皆为空的实例。而我们升级后的服务是使用的自动配置,仅保留了reference的xml配置,其余属性均删除,难道是一个后初始化的ApplicationConfig覆盖了前者正常的ApplicationConfig实例?并且后者的配置均为空属性,因为我们使用的Multi配置,multi配置比非multi配置的前缀多了’s’,难道是某种冲突导致的?

问题推测1结论

结论并不是,因为在另一个服务里面使用同样的配置方式,依然是正常的,所以这个现象仅出现在了当前项目中。。。真得要炸了。。。

  1. // dubbo-consumer.xml 消费者xml配置,ApplicationConfig、RegistryConfig等等实例会按照dubbo规约读取配置参数填充配置属性
  2. <?xml version="1.0" encoding="UTF-8"?>
  3. <beans xmlns="http://www.springframework.org/schema/beans"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://code.alibabatech.com/schema/dubbo
  8. http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
  9. <dubbo:reference id="departProvider" interface="com.....genius.provider.DepartProvider" timeout="500"
  10. check="false" registry="commonRegistry"/>
  11. </beans>

问题推测2

可以看到一个是空实例一个是有属性的实例,那么问题一定出在了属性数据的绑定上,而我们知道bean的数据绑定前是会提前曝光对象的,这就有些接近符合了,因为依赖注入时为了防止循环依赖spring提供了提前曝光对象,就是在bean还未完成初始化前曝光,这样bean中注入的依赖就有可能是空属性的依赖bean,而我们此时看到的正是这类现象。消费者中注入的ApplicationConfig所有的属性均为null。那么我就进一步分析下为什么另外一个项目不会有这类现象,只有该项目会有,并且推测需要证明,我们一步步分析验证我们的推测

  1. dubbo的xml配置的解析是有DubboNamespaceHandler处理,只有在有指定xml配置的时候才会执行bean的注册,由于我们没有配置applicationConfig相关的配置,所以此处不会注册applicationConfig的bean定义
  2. @EnableDubbo注解的父注解@EnableDubboConfig指定的dubbo配置选择器DubboConfigConfigurationSelector,该选择器导入Multiple类,因为我们使用的是多配置类型
  3. Multiple类注解EnableDubboConfigBinding导入DubboConfigBindingRegistrar注册器,会将指定的类型bean定义注入并注入对应DubboConfigBindingBeanPostProcessor后置处
  4. DubboConfigBindingBeanPostProcessor后置处理中可以看到在bean初始化之前会进行数据绑定

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

    1. if (beanName.equals(this.beanName) && bean instanceof AbstractConfig) {
    2. init();
    3. AbstractConfig dubboConfig = (AbstractConfig) bean;
    4. dubboConfigBinder.bind(prefix, dubboConfig);
    5. if (log.isInfoEnabled()) {
    6. log.info("The properties of bean [name : " + beanName + "] have been binding by prefix of " +
    7. "configuration properties : " + prefix);
    8. }
    9. }
    10. return bean;

    }
    // databinder将配置与bean绑定
    @Override
    public void bind(String prefix, C dubboConfig) {

    1. DataBinder dataBinder = new DataBinder(dubboConfig);
    2. // Set ignored*
    3. dataBinder.setIgnoreInvalidFields(isIgnoreInvalidFields());
    4. dataBinder.setIgnoreUnknownFields(isIgnoreUnknownFields());
    5. // Get properties under specified prefix from PropertySources
    6. Map<String, Object> properties = getSubProperties(getPropertySources(), prefix);
    7. // Convert Map to MutablePropertyValues
    8. MutablePropertyValues propertyValues = new MutablePropertyValues(properties);
    9. // Bind
    10. dataBinder.bind(propertyValues);

    }

然后我们断点进入发现在消费者reference引用get时仅绑定了registryConfig属性,没有绑定applicationConfig。为什么会有这类现象,我会再继续分析errorLogMonitorProvider对象bean的注入流程,(通过Configuration注解类的方法定义的bean)
根据异常的堆栈我们可以很快的定位到ConstructorResolver.instantiateUsingFactoryMethod方法中抛出了异常,我们只需要关心此时实例化该bean注入的args(也就是依赖的applicationConfig bean、registryConfig bean)的获取了流程

  1. beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
  2. @Override
  3. public Object run() {
  4. return beanFactory.getInstantiationStrategy().instantiate(
  5. mbd, beanName, beanFactory, fb, factoryMethod, args);
  6. }
  7. }, beanFactory.getAccessControlContext());

可以看到该参数的获取过程还是很复杂的,简单整理下流程

instantiateUsingFactoryMethod bean参数注入流程

Configuration注解类方法定义bean参数注入流程

  1. 遍历工厂类的所有方法(工厂方法)
  2. 根据工厂方法获取方法入参
  3. 根据入参创建argsHolder参数持有者,最终选出最接近构造方法入参的参数作为入参进行bean创建

我们要关心的正是该参数的获取流程
创建参数持有对象流程createArgumentArray

  1. 跟据索引获取indexedArgumentValues缓存中索引的valueHolder,如果获取不到再去genericArgumentValues缓存中获取
  2. 如果beanclass构造器不存在参数则返回按照类型注入,如果不是_AUTOWIRE_CONSTRUCTOR注入类型或者参数类型长度等于构造器参数长度,则尝试从_genericArgumentValues缓存中获取
  3. 如果均不是上述两种情景,则将候选方法Method与参数下标封装为MethodParameter类型,根据方法参数MethodParameter解析注入的bean实例resolveAutowiredArgument

解析注入参数bean:resolveAutowiredArgument
该bean正是我们实例化工厂类中的bean时工厂方法的注入参数,正是我们所关心的

  1. 使用工厂解析依赖beanFactory.resolveDependency
  2. 如果依赖是_javaUtilOptionalClass、javaxInjectProviderClass、_ObjectFactory、ObjectProvider类型,则使用对应的提供者提供
  3. 如果注入点需要懒惰解析依赖目标代理则创建并返回getLazyResolutionProxyIfNecessary
  4. 否则执行依赖解析

依赖解析doResolveDependency

  1. 依赖(参数)或者依赖目标的方法参数MethodParameter存在Value注解则提取Value注解的值,如果存在Value注解,则返回注解对应的绑定的数据DataBinder
  2. 解析多bean类型(数组、集合、map类型)
  3. 根据参数类型获取bean列表findAutowireCandidates,如果返回为空则抛出异常NoSuchBeanDefinitionException
  4. 如果匹配到多个bean,则按照primary,其次Priority优先级,最后兜底Fallback使用与参数名称相同的bean
  5. 如果只有一个bean匹配,如果bean是class类型则根据类型与bean名称从工厂获取,否则直接返回bean实例

根据类型查找候选bean:findAutowireCandidates

  1. 根据类型ApplicationConfig获取候选bean名称列表
  2. 从resolvableDependencies缓存中获取类型匹配的bean添加至result(返回的匹配bean)
  3. 如果不是自引用并且是自动注入的bean(是class的实例(例如:ApplicationConfig.class)则是true,或者Autowired、Qualifier注解等)
  4. 如果依赖描述是MultiElementDescriptor或者工厂单例singletonObjects缓存中包含候选bean(按照beandefinition中的beanName判断),则按照依赖描述获取bean resolveCandidate,否则从工厂中按照类型以及beanName获取
  5. DependencyDescriptor类型依赖描述获取bean实际调用工厂按照名称以及类型获取bean,此时返回了早期曝光对象,也就出现了属性均为null未注入的情况。也就验证了我们的推测

问题推测2结论

正如我们所推测的一样,注入ErrorLogMonitorPorvider时ApplicationConfig获取的是一个未完成初始化的对象,导致了开始提到的异常

思考

1.为什么registry注入的是完成初始化的对象

我们根这断点可以看到,singleObjects缓存中并没有对应registryConfig的对象,为什么applicationConfig会出现在singleObjects中呢?我们跟着堆栈可以看到问题出在ReferenceBean对象中的afterPropertiesSet,里面对相关的DubboConfig配置会判断是否为空,如果是则创建并且指定了初始化为懒惰加载,当然registry也是同样的方式,但是此时DubboConfigBinding没有加载至上下文。在baseRegistry加载时,Bing已经加载至上下文,会在postprocess中进行属性绑定

  1. if (getApplication() == null
  2. && (getConsumer() == null || getConsumer().getApplication() == null)) {
  3. Map<String, ApplicationConfig> applicationConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class, false, false);
  4. if (applicationConfigMap != null && applicationConfigMap.size() > 0) {
  5. ApplicationConfig applicationConfig = null;
  6. for (ApplicationConfig config : applicationConfigMap.values()) {
  7. if (config.isDefault() == null || config.isDefault().booleanValue()) {
  8. if (applicationConfig != null) {
  9. throw new IllegalStateException("Duplicate application configs: " + applicationConfig + " and " + config);
  10. }
  11. applicationConfig = config;
  12. }
  13. }
  14. if (applicationConfig != null) {
  15. setApplication(applicationConfig);
  16. }
  17. }
  18. }

2.为什么另外一个项目同样的配置与代码可以获取到初始化完成的对象

可以去另外一个项目看到,该项目在加载配置时,所有的DubboConfigBindingBeanPostProcessor已经加载至上下文,所以所有的对象的属性都在初始化之前就完成了属性填充,再其他bean从提前曝光的对象中获取依赖时,虽然对象可能没有完成初始化,但是关键属性都已经在前置处理中进行了填充

3.为什么ApplicationConfig等bean已经创建初始化完成并缓存至singleObjects中,却没有调用BeanPostProcessor处理bean的自定BeanPostProcessor动作?

因为在此时BeanPostProcessor还没有注入至上下文

问题总结

出现问题的原因主要是因为DubboConfigDataBindingBeanPostProcessor的注入时间点不同。正常的项目在referenceConfig注入时上下文中已经注入了数据绑定的后置处理,所以在判断配置为空时会自动绑定配置。但是出现问题的项目中该后置处理的注入过晚导致,获取到ApplicationConfig等配置的关键属性为空没有后置处理根据环境配置对其进行绑定,导致无法正确的创建引用bean。
我们比对两个项目,发现确实web项目的上下文在注册BeanPostProcessor时,applicationConfig以及相关联的RegistryConfig已经缓存至singletonObjects(绑定dubbo配置属性的后置处理器对它们未生效)。而非web项目的上下文在注册BeanPostProcessorProcessor时,是没有任何bean注册至singletonObjects(绑定dubbo配置属性的后置处理器对它们后面注册初始化时会生效)。
所以非web项目可以正常的使用dubbo-api以及dubbo.xml混合配置的方式,而web项目不可以

问题解决

定位问题后就断点跟进查看是谁导致的在beanPostProcessor未注册之前便将Dubbo配置bean注入至应用上下文,最终发现是spring-boot-devtools这个家伙,啊啊啊啊啊,删除该依赖后,服务正常启动。罪魁祸首就是这个家伙:DevToolsDataSourceAutoConfiguration

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-devtools</artifactId>
  4. <optional>true</optional>
  5. </dependency>

发表评论

表情:
评论列表 (有 0 条评论,106人围观)

还没有评论,来说两句吧...

相关阅读