其他分享
首页 > 其他分享> > dubbo自动装配记录-盲点记录

dubbo自动装配记录-盲点记录

作者:互联网

记录下dubbo的自动装配分析遇到的盲点

盲点一

自动装配在dubbo-spring-boot-autoconfigure-0.2.1.RELEASE.jar\META-INF\spring.factories文件内

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.boot.dubbo.autoconfigure.DubboAutoConfiguration

DubboAutoConfiguration在有dubbo开头的配置下启用,里面的@Bean很容易理解,这里只是记录它的两个内部类SingleDubboConfigConfiguration、MultipleDubboConfigConfiguration的装配过程,涉及到自己知识盲点。

/**
     * Single Dubbo Config Configuration
     *
     * @see EnableDubboConfig
     * @see DubboConfigConfiguration.Single
     */
@EnableDubboConfig
protected static class SingleDubboConfigConfiguration {
}

/**
     * Multiple Dubbo Config Configuration , equals @EnableDubboConfig.multiple() == <code>true</code>
     *
     * @see EnableDubboConfig
     * @see DubboConfigConfiguration.Multiple
     */
@ConditionalOnProperty(name = MULTIPLE_CONFIG_PROPERTY_NAME, havingValue = "true")
@EnableDubboConfig(multiple = true)
protected static class MultipleDubboConfigConfiguration {
}

在分析时候,发现两个内部类@EnableDubboConfig上的@Import(DubboConfigConfigurationRegistrar.class)比DubboAutoConfiguration类内的@Bean注解的方法先执行,这个原因涉及到了以前分析springboot自动装配机制当时没有完全理解的地方。

springboot 源码分析见https://www.cnblogs.com/zhangyjblogs/p/14438444.html#configurationclasspostprocessor源码分析

自己以前详细分析了springboot原理,但是现在在复习dubbo自动装配启动时候,对于两个内部类SingleDubboConfigConfiguration、MultipleDubboConfigConfiguration在什么时候进行装配的,竟然不清楚,因此详细分析下。

springboot的自动装配是在ConfigurationClassPostProcessor进行处理,在org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClass, SourceClass)方法内processMemberClasses方法实际就是处理内部类,详细分析下这个方法。

/**
	 * Register member (nested) classes that happen to be configuration classes themselves.
	 */
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {//传入的配置类是DubboAutoConfiguration
    Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();//获取DubboAutoConfiguration内部类,即SingleDubboConfigConfiguration、MultipleDubboConfigConfiguration
    if (!memberClasses.isEmpty()) {//存在内部类
        List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
        for (SourceClass memberClass : memberClasses) {
            if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) && //内部类被@Configuration、@Component、@Import、@Bean注解
                !memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {//内部类className不等于配置类className名称
                candidates.add(memberClass);//内部类加入到候选集合
            }
        }
        OrderComparator.sort(candidates);
        //对每个内部类进行处理
        for (SourceClass candidate : candidates) {
            if (this.importStack.contains(configClass)) {
                this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
            }
            else {
                this.importStack.push(configClass);
                try {
                    processConfigurationClass(candidate.asConfigClass(configClass));//处理DubboAutoConfiguration内部类上的配置类、@Import
                }
                finally {
                    this.importStack.pop();
                }
            }
        }
    }
}

关键代码是Collection<SourceClass> memberClasses = sourceClass.getMemberClasses(),获取内部类,下面看这个方法

//org.springframework.context.annotation.ConfigurationClassParser.SourceClass.getMemberClasses()
//获取内部类
public Collection<SourceClass> getMemberClasses() throws IOException {
    Object sourceToProcess = this.source;
    if (sourceToProcess instanceof Class) {
        Class<?> sourceClass = (Class<?>) sourceToProcess;
        try {
            Class<?>[] declaredClasses = sourceClass.getDeclaredClasses();//获取内部类
            List<SourceClass> members = new ArrayList<>(declaredClasses.length);
            for (Class<?> declaredClass : declaredClasses) {
                members.add(asSourceClass(declaredClass));//内部类包装为SourceClass并添加到集合返回
            }
            return members;
        }
        catch (NoClassDefFoundError err) {
            // getDeclaredClasses() failed because of non-resolvable dependencies
            // -> fall back to ASM below
            sourceToProcess = metadataReaderFactory.getMetadataReader(sourceClass.getName());
        }
    }

    // ASM-based resolution - safe for non-resolvable classes as well
    //this.source非class类型,比如是 com.alibaba.boot.dubbo.autoconfigure.DubboAutoConfiguration.MultipleDubboConfigConfiguration包装而成的SimpleMetadataReader
    MetadataReader sourceReader = (MetadataReader) sourceToProcess;
    String[] memberClassNames = sourceReader.getClassMetadata().getMemberClassNames();//获取内部类名称
    List<SourceClass> members = new ArrayList<>(memberClassNames.length);
    for (String memberClassName : memberClassNames) {
        try {
            members.add(asSourceClass(memberClassName));//内部类包装为SourceClass并添加到集合返回
        }
        catch (IOException ex) {
            // Let's skip it if it's not resolvable - we're just looking for candidates
            if (logger.isDebugEnabled()) {
                logger.debug("Failed to resolve member class [" + memberClassName +
                             "] - not considering it as a configuration class candidate");
            }
        }
    }
    return members;
}

如果是Class类型,直接通过Class方法getDeclaredClasses()获取内部类,这个简单。

如果非Class类型,直接通过sourceReader.getClassMetadata().getMemberClassNames()获取内部类名称,sourceReader.getClassMetadata()返回的是AnnotationMetadataReadingVisitor,获取内部类代码是

//org.springframework.core.type.classreading.ClassMetadataReadingVisitor.getMemberClassNames()
@Override
public String[] getMemberClassNames() {
    return StringUtils.toStringArray(this.memberClassNames);
}

发现内部类名已经是在AnnotationMetadataReadingVisitor存在的。那么就需要找AnnotationMetadataReadingVisitor的实例化阶段内部类的来源了。

通过查找发现ClassMetadata注释发现,该接口是由MetadataReader#getClassMetadata()创建,具体是SimpleMetadataReader实例化时候创建。代码如下

final class SimpleMetadataReader implements MetadataReader {

	private final Resource resource;

	private final ClassMetadata classMetadata;

	private final AnnotationMetadata annotationMetadata;

	SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {//resource是org.springframework.core.io.Resource
		InputStream is = new BufferedInputStream(resource.getInputStream());
		ClassReader classReader;
		try {
			classReader = new ClassReader(is);//创建字节码读取工具类ClassReader
		}
		catch (IllegalArgumentException ex) {
			throw new NestedIOException("ASM ClassReader failed to parse class file - " +
					"probably due to a new Java class file version that isn't supported yet: " + resource, ex);
		}
		finally {
			is.close();
		}

		AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader);//创建AnnotationMetadataReadingVisitor,是个ClassMetadata,也是AnnotationMetadata
		classReader.accept(visitor, ClassReader.SKIP_DEBUG);//字节码处理,解析字节码,把类结构解析到AnnotationMetadataReadingVisitor

		this.annotationMetadata = visitor;
		// (since AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor)
		this.classMetadata = visitor;
		this.resource = resource;
	}
}    

ClassMetadata、AnnotationMetadata接口类关系图如下:

20210524225600

org.springframework.core.io.Resource:表示类资源文件

ClassReader:字节码读取工具类,创建例子,new ClassReader(new BufferedInputStream(org.springframework.core.io.Resource.getInputStream())); 读取字节码并把类结构解析到AnnotationMetadataReadingVisitor,代码:classReader.accept(new AnnotationMetadataReadingVisitor(classLoader), ClassReader.SKIP_DEBUG); 类似ASM的ClassReader

MetadataReader:访问类元数据的外观模式接口,使用org.springframework.asm.ClassReader进行读取

SimpleMetadataReader:MetadataReader的具体实现,简单的元数据读取实现。使用ClassReader读取字节流并解析类结构到AnnotationMetadataReadingVisitor

ClassMetadata:class元数据接口,提供了获取类属性信息的能力。

​ getClassName:获取类名

​ isInterface:是否是接口

​ isAnnotation:是否是注解

​ isAbstract:是否是抽象类

​ isConcrete:是否是具体类,即非接口非抽象类

​ isFinal:是否被Final标识

​ isIndependent:是否是个独立类,确定基础类是否独立,即是否它是顶级类或嵌套类(静态内部类),可以与封闭类无关地构造

​ hasEnclosingClass:是否是内部类

​ getEnclosingClassName:返回内部类名

​ hasSuperClass:是否有超类

​ getSuperClassName:返回超类

​ getInterfaceNames:返回当前类实现的接口列表

​ getMemberClassNames:返回此类内部的成员类/接口(即该类里面的内部类或内部接口)

AnnotationMetadata:注解类的元信息,注解相较于普通类,多一些属性,因此又增加了此接口用于描述注解类元信息。

​ getAnnotationTypes:获取此注解类上的所有注解的全限名称

​ getMetaAnnotationTypes:根据传入的元注解的完全限定的类名获取此注解类上的所有元注解类型名称

​ hasAnnotation:此注解类是否被传入参数annotationName注解

​ hasMetaAnnotation:此注解类是否被指定的metaAnnotationName元注解注解

​ hasAnnotatedMethods:此注解类任何方法被指定的annotationName注解

​ getAnnotatedMethods:返回此注解类内被annotationName注解方法集合

AnnotatedTypeMetadata:注解类型元数据

​ isAnnotated:指定的元素是否被指定的annotationName注解

​ getAnnotationAttributes:检索给定类型的注释的属性(如果有的话),同时还要考虑对组合注释的属性覆盖。

​ getAllAnnotationAttributes:与getAnnotationAttributes功能相同,但是不进行属性覆盖

ClassMetadataReadingVisitor:ASM类访问者,该访问者仅查找类名称和实现的类型,并通过org.springframework.core.type.ClassMetadata接口将其公开。仅仅表示普通类,它的属性是表示类的信息。以DubboAutoConfiguration为例

​ 属性className:类名,比如是com.alibaba.boot.dubbo.autoconfigure.DubboAutoConfiguration

​ 属性isInterface:是否是接口,false

​ 属性isAnnotation:是否是注解,false

​ 属性isAbstract:是否是abstract,false

​ 属性isFinal:是否标注了Final,false

​ 属性enclosingClassName:null

​ 属性independentInnerClass:false

​ 属性superClassName:父类,java.lang.Object

​ 属性interfaces:实现的接口,null

​ 属性memberClassNames:成员内部类/接口,[com.alibaba.boot.dubbo.autoconfigure.DubboAutoConfiguration$MultipleDubboConfigConfiguration, com.alibaba.boot.dubbo.autoconfigure.DubboAutoConfiguration$SingleDubboConfigConfiguration]

AnnotationMetadataReadingVisitor:功能同ClassMetadataReadingVisitor,但是这个又支持注解类。继续以com.alibaba.boot.dubbo.autoconfigure.DubboAutoConfiguration为例

​ 属性annotationSet:此注解类上的注解集合 [org.springframework.context.annotation.Configuration, org.springframework.boot.autoconfigure.condition.ConditionalOnProperty, org.springframework.boot.autoconfigure.condition.ConditionalOnClass]

​ 属性metaAnnotationMap:此注解类上注解的注解集合(不包含元注解) {org.springframework.context.annotation.Configuration=[org.springframework.stereotype.Component, org.springframework.stereotype.Indexed], org.springframework.boot.autoconfigure.condition.ConditionalOnProperty=[org.springframework.context.annotation.Conditional], org.springframework.boot.autoconfigure.condition.ConditionalOnClass=[org.springframework.context.annotation.Conditional]}

​ 属性attributesMap:此注解类所有注解的属性集合(不包含元注解) 比如{org.springframework.context.annotation.Configuration=[{value=org.springframework.core.annotation.AnnotationUtils$DefaultValueHolder@f91da5e}], org.springframework.stereotype.Component=[{value=}], org.springframework.stereotype.Indexed=[{}], org.springframework.boot.autoconfigure.condition.ConditionalOnProperty=[{prefix=dubbo, name=[enabled], matchIfMissing=true, havingValue=true, value=org.springframework.core.annotation.AnnotationUtils$DefaultValueHolder@eca6a74}], org.springframework.context.annotation.Conditional=[{value=[class org.springframework.boot.autoconfigure.condition.OnPropertyCondition]}, {value=[class org.springframework.boot.autoconfigure.condition.OnClassCondition]}], org.springframework.boot.autoconfigure.condition.ConditionalOnClass=[{value=[Lcom/alibaba/dubbo/config/AbstractConfig;], name=org.springframework.core.annotation.AnnotationUtils$DefaultValueHolder@79fd6f95}]}

​ 属性methodMetadataSet:此注解类上的方法集合 [org.springframework.core.type.classreading.MethodMetadataReadingVisitor@6c9320c2, org.springframework.core.type.classreading.MethodMetadataReadingVisitor@3414a8c3, org.springframework.core.type.classreading.MethodMetadataReadingVisitor@cf518cf] 因为DubboAutoConfiguration只有三个方法

StandardClassMetadata:使用标准反射生成一个Class,仅支持普通类

​ 属性introspectedClass:使用标准反射生成的class

StandardAnnotationMetadata:使用标准反射生成一个Class,支持注解类

ClassVisitor:功能同ASM的ClassVisitor,提供访问java class的能力。

以上是spring对ClassMetadata的处理过程,现在回到dubbo自动装配,在ConfigurationClassPostProcessor执行processMemberClasses的时候,DubboAutoConfiguration有两个member类,而且这两个内部类上有@Import,因此@Import(DubboConfigConfigurationRegistrar.class)优先于@Bean serviceAnnotationBeanPostProcessor(Environment environment)方法执行。这也揭开了自己的疑惑。

总结下获取ClassMetadata整个流程,代码如下

//className=com.alibaba.boot.dubbo.autoconfigure.DubboAutoConfiguration
String resourcePath = "classpath:" + ClassUtils.convertClassNameToResourcePath(className) + ".class";
Resource resource = this.resourceLoader.getResource(resourcePath);	//resourceLoader即DefaultResourceLoader
InputStream is = new BufferedInputStream(resource.getInputStream());
ClassReader classReader = new ClassReader(is);
AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader);
classReader.accept(visitor, ClassReader.SKIP_DEBUG);//解析字节流,保存class信息到AnnotationMetadataReadingVisitor
//结果visitor就是ClassMetadata,可以获取内部类等信息

Class获取内部类参考 JDK反射类Class记录 https://www.cnblogs.com/zhangyjblogs/p/14819554.html

盲点二

DubboAutoConfiguration自动装配了DubboConfigConfiguration.Single,那么DubboConfigConfiguration.Single上的注解@EnableDubboConfigBindings是如何装配的呢?答案是在org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(BeanDefinitionRegistry)内的do{...}while内,判断注册的bean数量有变化,则会进一步解析配置类。代码如图

image-20210528002655321

因为自动装配类DubboAutoConfiguration有内部类SingleDubboConfigConfiguration,上面有注解@EnableDubboConfig引入了@Import(DubboConfigConfigurationRegistrar.class),在DubboConfigConfigurationRegistrar自动注册了bean DubboConfigConfiguration.Single,因此bean的数量发生变化,继而递归去解析com.alibaba.dubbo.config.spring.context.annotation.DubboConfigConfiguration.Single上的@EnableDubboConfigBindings注解,最终执行DubboConfigBindingsRegistrar注册dubbo的内部XxxConfig bean,比如ApplicationConfig,RegistryConfig。

解答:

  1. SingleDubboConfigConfiguration、MultipleDubboConfigConfiguration有什么作用?

    目的是为了注册dubbo的config bean。这两个类作为自动装配类DubboAutoConfiguration有内部类,会引入注册bean DubboConfigConfiguration.Single、DubboConfigConfiguration.Multiple,从而注册dubbo配置bean,比如ApplicationConfig、RegistryConfig、ProtocolConfig等。

  2. @EnableDubboConfigBindings是如何绑定的?

    在ConfigurationClassPostProcessor.processConfigBeanDefinitions(BeanDefinitionRegistry)的do{...}while内,判断注册的bean数量有变化,则会进一步解析配置类DubboConfigConfiguration.Single、DubboConfigConfiguration.Multiple,继而引入DubboConfigBindingsRegistrar注册dubbo config bean。

标签:dubbo,记录,DubboAutoConfiguration,盲点,springframework,注解,org,class
来源: https://www.cnblogs.com/zhangyjblogs/p/14974767.html