SpringBoot 启动分析(四) — 注解驱动的 Bean 定义加载

栏目: Java · 发布时间: 6年前

内容简介:先抛出个问题:SpringBoot 允许通过注解根据某个类是否存在来决定配置,如我们也知道Spring 是如何实现这样的条件加载而不会抛出

1. 一个 Spring 加载类的问题

先抛出个问题:SpringBoot 允许通过注解根据某个类是否存在来决定配置,如

@Bean
@ConditionalOnClass(value = HikariDataSource.class)
public DataSource hikariDataSource() {
    return new HikariDataSource();
}

@Bean
@ConditionalOnClass(value = BasicDataSource.class)
public DataSource basicDataSource() {
    return new BasicDataSource();
}

我们也知道 ClassLoader 加载一个类时,如果这个类或这个类依赖的类找不到则会抛出 ClassNotFoundException

Spring 是如何实现这样的条件加载而不会抛出 ClassNotFoundException 异常?

2. Spring 解析配置类的过程

在第一篇已经介绍过 ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry 会注册所有的 Bean 定义。现在来仔细看看这个过程。

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    int registryId = System.identityHashCode(registry);
    if (this.registriesPostProcessed.contains(registryId)) {
        throw new IllegalStateException(
                "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
    }
    if (this.factoriesPostProcessed.contains(registryId)) {
        throw new IllegalStateException(
                "postProcessBeanFactory already called on this post-processor against " + registry);
    }
    this.registriesPostProcessed.add(registryId);

    processConfigBeanDefinitions(registry);
}

这个方法主要是做了防止重复注册的处理,具体的注册逻辑在 processConfigBeanDefinitions(registry)

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List<BeanDefinitionHolder> configCandidates = new ArrayList<BeanDefinitionHolder>();
    String[] candidateNames = registry.getBeanDefinitionNames();

    // 首先从 registry 找出已经注册的未处理的配置类的定义作为配置类候选
    for (String beanName : candidateNames) {
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
                ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
            }
        }
        else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }

    // Return immediately if no @Configuration classes were found
    if (configCandidates.isEmpty()) {
        return;
    }

    // 对配置类候选进行排序,以满足依赖配置依赖顺序
    // Sort by previously determined @Order value, if applicable
    Collections.sort(configCandidates, new Comparator<BeanDefinitionHolder>() {
        @Override
        public int compare(BeanDefinitionHolder bd1, BeanDefinitionHolder bd2) {
            int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
            int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
            return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
        }
    });

    // 找出 beanName 的生成器
    // Detect any custom bean name generation strategy supplied through the enclosing application context
    SingletonBeanRegistry sbr = null;
    if (registry instanceof SingletonBeanRegistry) {
        sbr = (SingletonBeanRegistry) registry;
        if (!this.localBeanNameGeneratorSet && sbr.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
            BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
            this.componentScanBeanNameGenerator = generator;
            this.importBeanNameGenerator = generator;
        }
    }

    // Parse each @Configuration class
    // 创建 ConfigurationClassParser 来解析候选配置类定义
    ConfigurationClassParser parser = new ConfigurationClassParser(
            this.metadataReaderFactory, this.problemReporter, this.environment,
            this.resourceLoader, this.componentScanBeanNameGenerator, registry);

    // 当前待解析的候选配置类定义
    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);

    // 当前已解析的配置类
    Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
    do {
        // 解析当前候选集里的所有配置类定义
        parser.parse(candidates);

        // 验证解析的结果
        parser.validate();

        // 从解析获取解析得到的所有配置类
        Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());

        // 移除已经处理过的配置类,因为下面要进行注册,防止重复注册
        configClasses.removeAll(alreadyParsed);

        // 创建 ConfigurationClassBeanDefinitionReader,用于注册配置类
        // reader 的创建应该是可以放到循环外的
        // Read the model and create bean definitions based on its content
        if (this.reader == null) {
            this.reader = new ConfigurationClassBeanDefinitionReader(
                    registry, this.sourceExtractor, this.resourceLoader, this.environment,
                    this.importBeanNameGenerator, parser.getImportRegistry());
        }

        // 注册配置类
        this.reader.loadBeanDefinitions(configClasses);

        // 把本轮处理完的配置类加到已处理集合
        alreadyParsed.addAll(configClasses);

        // 清除已处理的候选集,复用这个集合
        candidates.clear();

        // 从 registry 找出新加入、未处理过的配置类定义,加入当前候选集
        if (registry.getBeanDefinitionCount() > candidateNames.length) {
            String[] newCandidateNames = registry.getBeanDefinitionNames();
            Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));
            Set<String> alreadyParsedClasses = new HashSet<String>();
            for (ConfigurationClass configurationClass : alreadyParsed) {
                alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
            }
            for (String candidateName : newCandidateNames) {
                if (!oldCandidateNames.contains(candidateName)) { // 过滤出新加入的
                    BeanDefinition bd = registry.getBeanDefinition(candidateName);
                    if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
                            !alreadyParsedClasses.contains(bd.getBeanClassName())) { // 找出未处理过的
                        candidates.add(new BeanDefinitionHolder(bd, candidateName));
                    }
                }
            }
            candidateNames = newCandidateNames;
        }
    } while (!candidates.isEmpty()); // 如果当前候选集不为空则继续下一轮处理

    // 至此,通过  Java  类配置的 bean 定义都加载并注册到容器

    // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
    if (sbr != null) {
        if (!sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
            sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
        }
    }

    if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
        ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
    }
}

上述方法并未涉及某个配置类定义如何解析得到配置类、注册配置类定义的细节这一层的逻辑如下:

1. 从 registry 收集配置类定义放到 Set<BeanDefinitionHolder> candidates 作为处理的开始源;

2. 创建 ConfigurationClassParser parser 用于解析新的配置类定义和 ConfigurationClassBeanDefinitionReader reader 用于注册新的配置类定义;

3. 初始化已解析集合 alreadyParsed

4. 用 parser 解析候选集 candidates ,进行验证;

5. 得到解析结果 Set<ConfigurationClass> configClasses ,移除已经处理过的 Set<ConfigurationClass> alreadyParsed

6. 用 reader 把收集到的配置类集合 configClasses 注册到 registry

7. 把 configClasses 加入已解析集合 alreadyParsed ;

8. 清空候选集 candidates ,重新收集候选集 candidates

9. 如果收集到的候选集 candidates 不为空,继续跌代步骤 4 。

现在来看如何解析候选集, ConfigurationClassParser.parse(Set<BeanDefinitionHolder> configCandidates)

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();

    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            if (bd instanceof AnnotatedBeanDefinition) {// 基于 Java 类配置的都是走这里
                parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
            }
            else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
                parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
            }
            else {
                parse(bd.getBeanClassName(), holder.getBeanName());
            }
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                    "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
        }
    }

    processDeferredImportSelectors();
}


protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
    // ....删掉了防止重复处理的逻辑

    // SourceClass 是一个简单的包装,允许被注解的源类以统一的方式进行处理,而不管它们是如何加载的

    // 递归处理配置类和它的超类
    SourceClass sourceClass = asSourceClass(configClass);
    do {
        // 处理给定的配置类,如果它有非 Object 的父类,则返回其父类,递归处理
        sourceClass = doProcessConfigurationClass(configClass, sourceClass);
    }
    while (sourceClass != null);

    this.configurationClasses.put(configClass, configClass);
}

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {

    // 首先递归处理内部嵌套类
    processMemberClasses(configClass, sourceClass);

    // 处理 @PropertySource 注解,把资源文件加载到 environment 里
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), PropertySources.class,
            org.springframework.context.annotation.PropertySource.class)) {
        if (this.environment instanceof ConfigurableEnvironment) {
            processPropertySource(propertySource);
        } else {
            logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                    "]. Reason: Environment must implement ConfigurableEnvironment");
        }
    }

    // 处理 @ComponentScan 注解,如果有,立即执行扫描
    Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
    if (!componentScans.isEmpty() &&
            !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
        for (AnnotationAttributes componentScan : componentScans) {
            // 如果配置类有 @ComponentScan 注解,立即执行扫描
            Set<BeanDefinitionHolder> scannedBeanDefinitions =
                    this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
            // 检查扫描到的定义里是否有更多的配置类,有则递归 parse
            for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                if (bdCand == null) {
                    bdCand = holder.getBeanDefinition();
                }
                if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                    parse(bdCand.getBeanClassName(), holder.getBeanName());
                }
            }
        }
    }

    // 处理 @Import 注解
    processImports(configClass, sourceClass, getImports(sourceClass), true);

    // 处理 @ImportResource 注解
    if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
        AnnotationAttributes importResource =
                AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
        String[] resources = importResource.getStringArray("locations");
        Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
        for (String resource : resources) {
            String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
            configClass.addImportedResource(resolvedResource, readerClass);
        }
    }

    // 处理 @Bean 注解的方法
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }

    // 处理接口的默认方法
    processInterfaces(configClass, sourceClass);

    // 如果有,处理父类
    if (sourceClass.getMetadata().hasSuperClass()) {
        String superclass = sourceClass.getMetadata().getSuperClassName();
        if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
            this.knownSuperclasses.put(superclass, configClass);
            // Superclass found, return its annotation metadata and recurse
            return sourceClass.getSuperClass();
        }
    }

    // 没有父类,处理完成
    return null;
}

解析一个配置类的大致过程:

  1. 递归处理所有嵌套类;
  2. 处理源类上 @PropertySource 注解;
  3. 处理 @ComponentScan 注解;
  4. 处理 @Import 注解,通过自定义的 ImportSelector/ImportBeanDefinitionRegistrar 接口实现来加载bean定义;
  5. 处理 @ImportResource 注解;
  6. 处理所有有 @Bean 注解的方法,方法的信息封装在 MethodMetadata ,与所在的配置类一起封装成 BeanMethod 添加到配置类的 beanMethods 集合里;
  7. 递归处理所实现的 interfaces 上的默认方法;
  8. 如果非 Object、非 java 自带类的父类,则返回进行递归处理;

上面的解析过程中有个很重要的角色是 SourceClass ,表示 SpringBoot 自己解析到的源文件信息。

// SourceClass 是一个简单的包装,允许被注解的源类以统一的方式进行处理,而不管它们是如何加载的

// 递归处理配置类和它的超类
SourceClass sourceClass = asSourceClass(configClass);
do {
    // 处理给定的配置类,如果它有非 Object 的父类,则返回其父类,递归处理
    sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);

在上述的递归处理过程中,sourceClass 一开始是配置类本身对应的源类的信息,后续是其父类的源类信息。

SourceClass

SourceClass 与 Class 啥区别?为什么需要它?

从上面代码的 SourceClass sourceClass = asSourceClass(configClass); 开始看:

SourceClass asSourceClass(String className) throws IOException {
    if (className.startsWith("java")) {
        // 对于核心Java类不使用ASM,因为肯定存在的,直接加载
        try {
            return new SourceClass(this.resourceLoader.getClassLoader().loadClass(className));
        } catch (ClassNotFoundException ex) {
            throw new NestedIOException("Failed to load class [" + className + "]", ex);
        }
    }
    return new SourceClass(this.metadataReaderFactory.getMetadataReader(className));
}

metadataReaderFactory 用完整类名构造一个 Resource,然后这个 Resource 和 ClassLoader 来构造一个 SimpleMetadataReader。SimpleMetadataReader 通过 ClassReader 来读取这个类的字节码文件,再通过 ASM 工具 AnnotationMetadataReadingVisitor 进行解析,得到关于这个类的元数据。这个过程绕过了JDK 的类加载机制,也就避免了一开始那个可能出现类找不到的问题。

有兴趣的可以继续看下 SimpleMetadataReader, MetadataReader 的唯一实现类:

final class SimpleMetadataReader implements MetadataReader {
    private final Resource resource;
    private final ClassMetadata classMetadata;
    private final AnnotationMetadata annotationMetadata;

    SimpleMetadataReader(Resource resource, ClassLoader classLoader) throws IOException {
        InputStream is = new BufferedInputStream(resource.getInputStream());
        ClassReader classReader;
        try {
            classReader = new ClassReader(is);
        } 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);
        classReader.accept(visitor, ClassReader.SKIP_DEBUG);

        this.annotationMetadata = visitor;
        // (since AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor)
        this.classMetadata = visitor;
        this.resource = resource;
    }
    // 省略其他代码
}

以上所述就是小编给大家介绍的《SpringBoot 启动分析(四) — 注解驱动的 Bean 定义加载》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Alone Together

Alone Together

Sherry Turkle / Basic Books / 2011-1-11 / USD 28.95

Consider Facebookit’s human contact, only easier to engage with and easier to avoid. Developing technology promises closeness. Sometimes it delivers, but much of our modern life leaves us less connect......一起来看看 《Alone Together》 这本书的介绍吧!

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具