美文网首页dubbo源码解析与实战
dubbo源码分析 config层 从@Service注解看sp

dubbo源码分析 config层 从@Service注解看sp

作者: 语落心生 | 来源:发表于2019-08-12 11:37 被阅读0次
    3072.png

    今天看到之前dubbo项目重启动的时候,遗留下来的一个bug。看了下代码被修复了,觉得自己对于dubbo的源码还不够熟练,复现该用户场景如下

    假设我们有一个DemoService接口:

    public interface DemoService {
        String sayHello(String name);
        String sayHello2(String name);
    }
    

    通过配置注解的parameters参数来往url上附加额外参数

    @Service(
            version = "${demo.service.version}",
            application = "${dubbo.application.id}",
            protocol = "${dubbo.protocol.id}",
            registry = "${dubbo.registry.id}"
            , parameters = {"sayHello.timeout", "3100", "sayHello2.timeout", "5000"}
    )
    public class DefaultDemoService implements DemoService {
        @Override
        public String sayHello(String name) {
            System.out.println("get request");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "sayHello Hello, " + name + " (from Spring Boot)";
        }
     
        @Override
        public String sayHello2(String name) {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "sayHello2 Hello, " + name + " (from Spring Boot)";
        }
    }
    

    结果报错:

    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'ServiceBean:defaultDemoService:com.kingnet.blockdata.service.DemoService:${demo.service.version}': Initialization of bean failed; nested exception is org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type 'java.lang.String[]' to required type 'java.util.Map' for property 'parameters'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String[]' to required type 'java.util.Map' for property 'parameters': no matching editors or conversion strategy found
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:589) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:503) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$97/1279271200.getObject(Unknown Source) ~[na:na]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:760) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:869) ~[spring-context-5.0.7.RELEASE.jar:5.0.7.RELEASE]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550) ~[spring-context-5.0.7.RELEASE.jar:5.0.7.RELEASE]
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:395) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:327) ~[spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
        at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:137) [spring-boot-2.0.3.RELEASE.jar:2.0.3.RELEASE]
        at com.kingnet.blockdata.DubboProviderDemo.main(DubboProviderDemo.java:20) [classes/:na]
    Caused by: org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type 'java.lang.String[]' to required type 'java.util.Map' for property 'parameters'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String[]' to required type 'java.util.Map' for property 'parameters': no matching editors or conversion strategy found
        at org.springframework.beans.AbstractNestablePropertyAccessor.convertIfNecessary(AbstractNestablePropertyAccessor.java:590) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
        at org.springframework.beans.AbstractNestablePropertyAccessor.convertForProperty(AbstractNestablePropertyAccessor.java:604) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
        at org.springframework.beans.BeanWrapperImpl.convertForProperty(BeanWrapperImpl.java:219) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.convertForProperty(AbstractAutowireCapableBeanFactory.java:1660) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1616) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1363) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:580) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
        ... 15 common frames omitted
    Caused by: java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String[]' to required type 'java.util.Map' for property 'parameters': no matching editors or conversion strategy found
        at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:299) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
        at org.springframework.beans.AbstractNestablePropertyAccessor.convertIfNecessary(AbstractNestablePropertyAccessor.java:585) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
        ... 21 common frames omitted
    

    @Service里面的parameters是String[]类型,但是要映射到ServiceBean的parameters属性是一个Map<String,String>类型,没法自动转换映射上去,

    查看源码,ServiceBean是通过:
    com.alibaba.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationBeanPostProcessor#registerServiceBean

    我们来看到修复后的dubbo bean注册源码

    • 在sping bean的加载过程中,BeanDefinitionRegistry是用来注册BeanDefinition的. BeanDefinition就是Bean的配置元数据或Bean的描述信息, 比如Bean的属性值, 构造方法的参数值等. 上面的BeanFactory的BeanDefinition也是由它注册的.

    • BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor的扩展, 允许在BeanFactoryPostProcessor被调用之前对BeanDefinition做一些操作, 尤其是它可以注册BeanFactoryPostProcessorBeanDefinition. 它提供了一个方法postProcessBeanDefinitionRegistry(), 这个方法被调用的时候, 所有的BeanDefinition已经被加载了, 但是所有的Bean还没被创建.

    注意:

    • 所有的Bean生成都有个顺序: 定义 --> 创建 --> 初始化.
    • BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法在Bean被定义但还没被创建的时候执行.
    • BeanFactoryPostProcessor的postProcessBeanFactory方法在Bean被创建但还没被初始化的时候执行
    @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    
            Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
            if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
                registerServiceBeans(resolvedPackagesToScan, registry);
            } else {
                if (logger.isWarnEnabled()) {
                    logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
                }
            }
    
        }
    
    • dubbo注册ServiceBean的方法发现是拿到所有注解的信息
    • 把这个注解的信息附加到相应的注解类的属性上
    • 根据注解的信息,注解的接口类,和已经注册在上下文中的这个beanName的名称新建一个Servicebean
    • 之后把建立的ServiceBean注册到上下文中
    private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
                                         DubboClassPathBeanDefinitionScanner scanner) {
    
            Class<?> beanClass = resolveClass(beanDefinitionHolder);
    
            Annotation service = findServiceAnnotation(beanClass);
    
            /**
             * The {@link AnnotationAttributes} of @Service annotation
             */
            AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);
    
            Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
    
            String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
    
            AbstractBeanDefinition serviceBeanDefinition =
                    buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
    
            // ServiceBean Bean name
            String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);
    
            if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean
                registry.registerBeanDefinition(beanName, serviceBeanDefinition);
    
                if (logger.isInfoEnabled()) {
                    logger.info("The BeanDefinition[" + serviceBeanDefinition +
                            "] of ServiceBean has been registered with name : " + beanName);
                }
    
            } else {
    
                if (logger.isWarnEnabled()) {
                    logger.warn("The Duplicated BeanDefinition[" + serviceBeanDefinition +
                            "] of ServiceBean[ bean name : " + beanName +
                            "] was be found , Did @DubboComponentScan scan to same package in many times?");
                }
    
            }
    
        }
    

    这里就是将之前注册的bean类型按照类型判断,为每种类型建立相应的bean

    private AbstractBeanDefinition buildServiceBeanDefinition(Annotation serviceAnnotation,
                                                                  AnnotationAttributes serviceAnnotationAttributes,
                                                                  Class<?> interfaceClass,
                                                                  String annotatedServiceBeanName) {
    
            BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);
    
            AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
    
            MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
    
            String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
                    "interface", "interfaceName", "parameters");
    
            propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotation, environment, ignoreAttributeNames));
    
            // References "ref" property to annotated-@Service Bean
            addPropertyReference(builder, "ref", annotatedServiceBeanName);
            // Set interface
            builder.addPropertyValue("interface", interfaceClass.getName());
            // Convert parameters into map
            builder.addPropertyValue("parameters", convertParameters(serviceAnnotationAttributes.getStringArray("parameters")));
            // Add methods parameters
            List<MethodConfig> methodConfigs = convertMethodConfigs(serviceAnnotationAttributes.get("methods"));
            if (!methodConfigs.isEmpty()) {
                builder.addPropertyValue("methods", methodConfigs);
            }
    
           ......
    
            return builder.getBeanDefinition();
    
        }
    

    但这些类型从哪里来呢?我们刚刚讲的是beanDefinition转换为bean
    。在spring拓展Schema的时候,dubbo需要将META-INF/spring.handlers中的配置转换为beanDefinition,通过DubboNamespaceHandler获取xml文件的命名空间,对不同的节点进行解析

    public class DubboNamespaceHandler extends NamespaceHandlerSupport {
        static {
            // 确保系统中只存在一份解析处理器类定义
            Version.checkDuplicate(DubboNamespaceHandler.class);
        }
        public void init() {
            // DubboBeanDefinitionParser定义了如何解析dubbo节点信息
            // DubboBeanDefinitionParser的第一个参数是beanclass
            // 配置<dubbo:application>标签解析器
        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
            // 配置<dubbo:module>标签解析器
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
            //配置<dubbo:registry>标签解析器
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
            //配置<dubbo:monitor>标签解析器
        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
            //配置<dubbo:provider>标签解析器
        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
            //配置<dubbo:consumer>标签解析器
        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
            //配置<dubbo:protocol>标签解析器
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        //配置<dubbo:service>标签解析器
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        //配置<dubbo:refenrence>标签解析器
        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        //配置<dubbo:annotation>标签解析器
        registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true));
        }
    }
    
    
    

    相关文章

      网友评论

        本文标题:dubbo源码分析 config层 从@Service注解看sp

        本文链接:https://www.haomeiwen.com/subject/ahmyjctx.html