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

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

内容简介:先抛出个问题: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 定义加载》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

Web Design DeMYSTiFieD

Web Design DeMYSTiFieD

Willard, Wendy / 2010-11 / $ 24.86

Website Design just got a whole lot easier! This title helps you to learn the latest website development tools, techniques, and best practices. "Web Design Demystified" provides the hands-on help you ......一起来看看 《Web Design DeMYSTiFieD》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

SHA 加密
SHA 加密

SHA 加密工具

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

Markdown 在线编辑器