springboot2.0.3源码篇

  • 时间:
  • 浏览:0
  • 来源:幸运快3_快3下载app送28_幸运快3下载app送28

前言

  开心一刻   

    女儿: “妈妈,你如此漂亮,当年为何嫁给了爸爸呢?”

    妈妈: “当年你爸算不算穷嘛!‘

    女儿: “穷你还嫁给他!”

    妈妈: “那你会你会毕业参加工作,领导对他说,他是我的扶贫对象,我年轻理解错了,就嫁给他了!”

    女儿......

@Import注解应用

  应用开发中,当亲戚亲戚朋友的功能模块比较多时,往往会按模块或类别对Spring的bean配置文件进行管理,使配置文件模块化,更容易维护;spring3.0你会,对Spring XML bean文件进行拆分, 类事

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/801/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
 
    <import resource="config/user.xml"/>
    <import resource="config/role.xml"/>
    <import resource="config/permission.xml"/>
 
</beans>

  spring3.0及你会,引入了@Import注解,提供与Spring XML中的<import />元素等效的功能;spring4.2你会,@Import只支持导入配置类(@Configuration修饰的类、ImportSelector实现类和ImportBeanDefinitionRegistrar实现类),而spring4.2及你会不仅支持导入配置类,同時 也支持导入常规的java类(如普通的User类)

  示例地址:spring-boot-autoconfig,这一算不算配置,不让down下来运行,看一眼具体怎么才能 才能 配置即可

  运行测试用例,结果如下

  可还都后能 了了想看 ,Dog、Cat、Role、User、Permission的实例都肯能注册到了spring容器,也可是我说上述讲的@Import的4种辦法 算不算并能将实例注册到spring容器的

@Import注解原理

  @Import何以有如此强大的功能,背后肯定有某个团队在运作,而这一 团队是谁了,可是我spring;spring容器肯定在某个阶段有对@Import进行了外理,至于spring是在这一 你会对@Import进行了怎么才能 才能 的外理,亲戚亲戚朋友来跟一跟源码;ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor,如此它会在spring启动的refresh阶段被应用,亲戚亲戚朋友从refresh的invokeBeanFactoryPostProcessors辦法 过后开始

  ConfigurationClassPostProcessor

    注意此时spring容器中的bean定义与bean实例,数量非常少,亲戚亲戚朋友可还都后能 了了留心观察下

    一路跟下来,亲戚亲戚朋友来到processConfigBeanDefinitions辦法 ,该辦法 会创建有有三个小 ConfigurationClassParser对象,该对象会分析所有@Configuration注解的配置类,产生一组ConfigurationClass对象,你会从这组ConfigurationClass对象中加载bean定义

  ConfigurationClassParser

    主可是我parse辦法

    从启动类(示例中是com.lee.autoconfig.AutoConfigApplication)过后开始,递归解析配置类以及配置类的父级配置类;边跟边注意beanFactory中beanDefinitionMap的变化,ConfigurationClassParser对象有beanFactory的引用,属性全名是registry;亲戚亲戚朋友可还都后能 了了仔细看下doProcessConfigurationClass辦法

/**
 * 通过从源类中读取注解、成员和辦法

来构建有有三个小



完整版的配置类:ConfigurationClass
 * 注意返回值,是父级类或null(null蕴含

这一请况,没找到父级类或你会肯能外理完成)
 */
@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
        throws IOException {

    // 递归外理配置类内置的成员类
    processMemberClasses(configClass, sourceClass);

    // 外理配置类上所有@PropertySource注解
    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注解,包括@ComponentScans和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修饰的配置类,
            // 通常是从启动类所在的包(示例中是com.lee.autoconfig)过后开始扫描,扫描配置类(被@Configuration修饰的类)
            Set<BeanDefinitionHolder> scannedBeanDefinitions =
                    this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
            // 进一步检查通过配置类扫描得到的bean定义集,并在还都后能





了时递归解析
            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注解
    // 包括@Import支持的4种类型:ImportSelector、ImportBeanDefinitionRegistrar、@Configuration和普通java类
    // 普通java类会被按@Configuration辦法

外理
    processImports(configClass, sourceClass, getImports(sourceClass), true);

    // 外理配置类上所有的@ImportResource注解,xml辦法

的bean可是我其中之一
    AnnotationAttributes importResource =
            AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
    if (importResource != null) {
        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 != null && !superclass.startsWith("java") &&
                !this.knownSuperclasses.containsKey(superclass)) {
            this.knownSuperclasses.put(superclass, configClass);
            // Superclass found, return its annotation metadata and recurse
            return sourceClass.getSuperClass();
        }
    }

    // No superclass -> processing is complete
    return null;
}
View Code

    上述代码中写了相关注释,有兴趣的同学可还都后能 了了更进一步的去跟,这里我只跟下processImports辦法 ,肯能这一 与自动配置息息相关

    起始的ConfigurationClass包括:1、工程中所有亲戚亲戚朋友自定义的被@Configuration修饰的类,示例中就还都后能 了AnimalConfig;2、应用的启动类,示例中是:AutoConfigApplication。

    亲戚亲戚朋友自定义的ConfigurationClass一般不让蕴含 多级父级ConfigurationClass,类事AnimalConfig,就如此父级ConfigurationClass,解析就比较简单,亲戚亲戚朋友不让关注,但AutoConfigApplication就不一样了,他往往会被多个注解修饰,而这一 注解会牵扯出多个ConfigurationClass,还都后能 了递归外理所有的ConfigurationClass;上图中,亲戚亲戚朋友跟到了有有三个小 比较重要的类:AutoConfigurationImportSelector,实例化你会封装成了DeferredImportSelectorHolder对象,存插进了ConfigurationClassParser的deferredImportSelectors属性中

自动配置源码解析

  其他同学肯能有另有有三个小 的问題:哪来的AutoConfigurationImportSelector,它有这一 用? 客观莫急,亲戚亲戚朋友慢慢往下看

  亲戚亲戚朋友的应用启动类被@SpringBootApplication,它是个组合注解,详情如下

  相信亲戚亲戚朋友都想看 @Import(AutoConfigurationImportSelector.class)了,ConfigurationClassParser可是我从此解析到的AutoConfigurationImportSelector,至于AutoConfigurationImportSelector有这一 用,马上揭晓;亲戚亲戚朋友回到ConfigurationClassParser的parse辦法 ,后边还有个怪怪的要的辦法 :processDeferredImportSelectors,值得亲戚亲戚朋友完整版跟下

  processDeferredImportSelectors

    说的简单点,从类路径下的所有spring.facoties文件中读取完整版的自动配置类(spring.factories文件中org.springframework.boot.autoconfigure.EnableAutoConfiguration的值),你会筛选出满足条件的配置类,封装成ConfigurationClass,存插进ConfigurationClassParser的configurationClasses属性中

    说的完整版点,分有有三个小 辦法 进行说明

      selectImports辦法

        从类路径下的所有spring.facoties文件中读取org.springframework.boot.autoconfigure.EnableAutoConfiguration的所有值,此时获取的是全路径类名的数组,你会进行筛选过滤,1、先去重外理,肯能多个spring.factories中肯能发生重复的;2、你会剔除亲戚亲戚朋友配置的还都后能 了排除的类,包括@SpringBootApplication注解的exclude、excludeName,以及配置文件中的spring.autoconfigure.exclude;3、条件过滤,过滤出满足当时人条件注解的配置类。最终获取所有满足条件的自动配置类,示例蕴含 2有有三个小 。

        条件注解更完整版的信息请查看:spring-boot-2.0.3源码篇 - @Configuration、Condition与@Conditional,读取spring.facoties文件的完整版信息请查看:spring-boot-2.0.3启动源码篇一 - SpringApplication构造辦法

      processImports辦法

        这一 辦法 在解析ConfigurationClassParser的parse辦法 的你会肯能用到过了,可是我如此做说明,它着实 可是我用来外理配置类上的@Import注解的;上述selectImports辦法 解挥发性来的配置类,每个配置类后会 经过processImports辦法 外理,递归外理@Import注解,就与递归外理亲戚亲戚朋友的启动类的@Import注解一样,从而获取所有的自动配置类;springboot的自动配置可是我另有有三个小 实现的。

  此时还可是我获取了满足条件的自动配置类,配置类中的bean定义加载还如此进行,亲戚亲戚朋友回到ConfigurationClassPostProcessor的processConfigBeanDefinitions辦法 ,其蕴含 如下代码

// 各种辦法

的配置类的解析,包括springboot的自动配置 - @Import、AutoConfigurationImportSelector
parser.parse(candidates);
parser.validate();

Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
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());
}
this.reader.loadBeanDefinitions(configClasses);    // 将配置类中的bean定义加载到beanFactory

  至此,springboot的自动配置源码解析就完成了,有兴趣的可还都后能 了了更近一步的深入

总结

  1、各个辦法 之间的调用时序图如下,结合这一 时序图看后边的内容,更好看懂

  2、springboot自动配置底层依赖的是SpringFactoriesLoader和AutoConfigurationImportSelector;@EnableAutoConfiguration注解就像有有三个小 八爪鱼,抓取所有满足条件的配置类,你会读取其中的bean定义到spring容器,@EnableAutoConfiguration得以生效的关键组件关系图如下