栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java > SpringBoot

SpringBoot 之 EnableAutoConfiguration 实践和源码学习

SpringBoot 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringBoot 之 EnableAutoConfiguration 实践和源码学习

EnableAutoConfiguration 是SpringBoot的Enable系列中一个比较基础的的功能模块,现在我们就来学习如何使用,以及分析源码学习其工作原理

EnableAutoConfiguration 从名字也可以很容易看出来其功能是能够自动装配配置,在SpringBoot中如果需要为其他人提供SDK等接口使用,使用方本身必须实例化接口类才可调用,如果每一个使用方都单独去实例化该接口,必然导致使用成本的增加,EnableAutoConfiguration就能很好的解决这个问题,使用方通过这个就可以直接使用,避免额外操作。

1、EnableAutoConfiguration 源码学习

先提个问题,如果现在只能使用Spring framework,该如何实现类似的功能呢?
或许能想到的只有BPP,BeanPostProcessor或者BeanFactoryPostProcessor,只是他们处理的范畴不一样,BeanPostProcessor更多的是处理单个bean,而BeanFactoryPostProcessor是处理context上下文的

Spring包含了多种类型的BPP,在spring的生命周期的多个位置提供了对外的钩子便于扩展更多功能,关于BPP可以看看BPP的内容
在官方文档中,对BeanFactoryPostProcessor方法的简述也说的非常清楚,Modify the application context's internal bean factory after its standard initialization. All bean definitions will have been loaded, but no beans will have been instantiated yet. This allows for overriding or adding properties even to eager-initializing beans.

事实上,SpringBoot也确实是这样干的,ConfigurationClassPostProcessor 就是实例化了一个BeanDefinitionRegistryPostProcessor,从而拥有了修改context上下文的bean信息以及注册bean的功能

如下图由Spring的BPP处理器调用到ConfigurationClassPostProcessor,然后来到了AutoConfigurationimportSelector 类中

image

1.1、ConfigurationClassPostProcessor 处理

先了解下ConfigurationClassPostProcessor 这个BPP是如何被注入到spring容器的

在springboot启动学习笔记中 已经介绍了spring的context上下文创建是由context = createApplicationContext(); 实现的,深入该代码直到AnnotationConfigUtils 类可以发现其操作是如果没发现org.springframework.context.annotation.internalCommonAnnotationProcessor这个bean的name,那就添加一个ConfigurationClassPostProcessor bean,具体如下图

image

这样我们就清楚了ConfigurationClassPostProcessor 这个类是如何装载进spring容器中的,接下来就是ConfigurationClassPostProcessor这个类具体的调用操作了

ConfigurationClassPostProcessor 类

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List configCandidates = new ArrayList();
    String[] candidateNames = registry.getBeanDefinitionNames();    for (String beanName : candidateNames) {           // 遍历spring容器所有的beanName信息,此时还未装载业务bean,
           // 只有spring&springboot本身框架层次需要的一些特定bean存在(特别注意包含主启动类)
           // 这点在之前的关于springboot的启动学习笔记中已经介绍了
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);        if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
                ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {                // 确认该beandefinition是否存在org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass 键值对信息
                // 如果存在则假定是已经经过配置类处理过了
            if (logger.isDebugEnabled()) {
                logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
            }
        }        else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {               // 否则检查一下当前的beandefinition是否符合config 配置类
              // 具体实现原理就是获取到bean的注解信息,然后查看是否存在 @Configuration 注解类或者 @Bean 注解
              // 如果有,则返回true
              // 当然在这里只会有主启动类才包含了@Configuration 的信息
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }    // Return immediately if no @Configuration classes were found
    if (configCandidates.isEmpty()) {        return;
    }    // 一般情况下,到这里只会有主启动类一个configCandidates信息存在

    // Sort by previously determined @Order value, if applicable
    Collections.sort(configCandidates, new Comparator() {        @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;
        }
    });    // 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)) {             // 如果当前类不包含beanName 生成器 同时 context包含了单一的beanName生成器
             // 设置当前bean的生成器信息
            BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);            this.componentScanBeanNameGenerator = generator;            this.importBeanNameGenerator = generator;
        }
    }    // 生成配置解析类parses,开始解析每一个包含了@Configuration 的类
    ConfigurationClassParser parser = new ConfigurationClassParser(            this.metadataReaderFactory, this.problemReporter, this.environment,            this.resourceLoader, this.componentScanBeanNameGenerator, registry);

    Set candidates = new linkedHashSet(configCandidates);
    Set alreadyParsed = new HashSet(configCandidates.size());    do {
        parser.parse(candidates);        // 关键的地方来了,这里就会去解析真正包含了@Configuration 的类
        parser.validate();

        Set configClasses = new linkedHashSet(parser.getConfigurationClasses());        // 所有通过@Configuration 装载进来的类集合
        configClasses.removeAll(alreadyParsed);        // 已经装载的就移除掉

        // 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());
        }        // reader是配置类装载类beandefinition
        this.reader.loadBeanDefinitions(configClasses);        // 装载到Spring容器中,包含了那些使用@Bean的类信息
        alreadyParsed.addAll(configClasses);
            ......            // 到这里就可以认为@Configuration 的导入基本完成了}

真正进入到@Configuration 解析的入口处代码

ConfigurationClassParser 类

image

在经过processDeferredimportSelectors方法调用的之前,已经经过了parse处理,明确了@Configuration 包含的importSelectors 信息
如果需要自定义该注解则一定也要实现利用@import注解导入的importSelector 实现类

private void processDeferredimportSelectors() {
    List deferredimports = this.deferredimportSelectors;    this.deferredimportSelectors = null;
    Collections.sort(deferredimports, DEFERRED_import_COMPARATOR);    // 对获取的DeferredimportSelectorHolder 排序后进行遍历操作

    for (DeferredimportSelectorHolder deferredimport : deferredimports) {
        ConfigurationClass configClass = deferredimport.getConfigurationClass();        // configClass 就是主启动类
        try {
            String[] imports = deferredimport.getimportSelector().selectimports(configClass.getmetadata());            // 所有的需要导入的import类的selectimports方法执行,
            // 这个里面就是EnableAutoConfigurationimportSelector 
            // 具体的EnableAutoConfigurationimportSelector里面的selectimports后面说
            processimports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);            // 把获取的配置类信息进一步迭代处理,因为存在在类中包含了配置类的情况
            // 不过需要注意,这时候并未往spring容器中注入
        }        catch (BeanDefinitionStoreException ex) {            throw ex;
        }        catch (Throwable ex) {            throw new BeanDefinitionStoreException(                    "Failed to process import candidates for configuration class [" +
                    configClass.getmetadata().getClassName() + "]", ex);
        }
    }
}
1.2、EnableAutoConfigurationimportSelector 的 selectimports 执行

来到AutoConfigurationimportSelector类

public String[] selectimports(Annotationmetadata annotationmetadata) {    if (!isEnabled(annotationmetadata)) {          // 如果注解原信息未激活,则不可用,直接返回空
        return NO_importS;
    }    try {
        AutoConfigurationmetadata autoConfigurationmetadata = AutoConfigurationmetadataLoader
                .loadmetadata(this.beanClassLoader);
        AnnotationAttributes attributes = getAttributes(annotationmetadata);
        List configurations = getCandidateConfigurations(annotationmetadata,
                attributes);        // 利用SpringFactoriesLoader 获取系统中所有的meta-INF/spring.factories 的 EnableAutoConfiguration 的键值对信息,其中就包含了上面我们自定义的类信息
        configurations = removeDuplicates(configurations);        // 存在多处地方可能注册了相同的类信息,去重处理
        configurations = sort(configurations, autoConfigurationmetadata);
        Set exclusions = getExclusions(annotationmetadata, attributes);        // 匹配出包含exclude、excludeName 的列表信息,后续移除该config
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = filter(configurations, autoConfigurationmetadata);        // 总之经过各种操作,最后产出了可用的配置类列表
        fireAutoConfigurationimportEvents(configurations, exclusions);        // 配置导入的事件触发
        return configurations.toArray(new String[configurations.size()]);        // 返回最后的配置类列表
    }    catch (IOException ex) {        throw new IllegalStateException(ex);
    }
}



作者:jwfy
链接:https://www.jianshu.com/p/582f48bea5a2


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/235442.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号