SpringBoot源码流程解析(重置版)

spring boot 本来应该在spring 结束之后就写的,最开始是被其它事情耽误了,后面就一直拖到了现在,这个框架可以说是目前市面最火的框架,不管是使用它之后的快速开发,还是支持大多数的中间件自动配置,又或者是强大的兼容性等等,都是企业项目优先选择它的理由。

本次就简单聊聊spring boot 的那一些八股文,再详细走一遍自动配置的源码流程。

SpringBoot简介

springBoot 本身是对spring 的一种封装,它的出现就是为了更加方便、快速的开发Spring 的项目。它的设计是为了让你尽可能快的跑起来Spring 应用程序,并且尽可能减少你的配置文件。

springBoot 基于Spring4.0设计,不仅继承了Spring框架原有的优秀特性,而且还通过简化配置来进一步简化了Spring 应用的整个搭建和开发过程。另外SpringBoot 通过集成大量的框架使得依赖包的版本冲突,以及引用的不稳定性等问题得到了很好的解决。

SpringBoot主要特性

  1. SpringBoot Starter:他将常用的依赖分组进行了整合,将其合并到一个依赖中,这样就可以一次性添加到项目的Maven 或Gradle 构建中;
  2. 使编码变得简单,SpringBoot 采用 JavaConfig 的方式对Spring 进行配置,并且提供了大量的注解,极大的提高了工作效率。
  3. 自动配置:SpringBoot 的自动配置特性利用了Spring 对条件化配置的支持,合理地推测应用所需的bean 并自动化配置他们;
  4. 使部署变得简单,SpringBoot 内置了三种Servlet容器,Tomcat,Jetty,undertow 我们只需要一个Java 的运行环境就可以跑SpringBoot 的项目了,SpringBoot 的项目可以打成一个jar包。

源码流程解析

上述也说了,spring boot 是spring 的封装,所以代码流程方面IOC 和AOP 之类的还是spring 部分的内容,spring boot 主要做的就是一些自动配置之类的东西,同时它也提供出了指定方案让其余的中间件自己去兼容spring boot 的自动配置。我们这里主要看的话也就是自动配置的内容。

源码就不用再引入了,随便找一个spring boot 的项目都能直接去看。

spring boot 的开始是从它的启动类开始的,也就是被@SpringBootApplication 注解所修饰的对象,具体点就是从这里的main 方法开始的,这里也就是调用了SpringApplication.run

spring boot 自动配置的两个重点也就是@SpringBootApplication 注解和这个run 方法,这里我们先讲注解。

@SpringBootApplication 注解解析

排除三个spring 的标准注解,还有一个@Inherited 注解,我们从@SpringBootConfiguration 注解开始看。

@SpringBootConfiguration:表示这个是spring boot 的启动配置类,这个里面上层注解是@Configuration

@EnableAutoConfiguration:自动配置实现的主要注解;

@ComponentScan:标注扫描路径的注解。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
    
     @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
    
    

显而易见我们下面继续跟的就是@EnableAutoConfiguration 注解。

还是一样的,排除四个不必要注解,主要看最下面两个注解即可。

@AutoConfigurationPackage:这里主要使用@import 注解,调用AutoConfigurationPackages.Registrar 对象的registerBeanDefinitions 方法,将启动对象所在的所在包信息,构建成一个BeanDefinition 对象存入到BeanFcatory 的BeanDefinitionMap 中,后续再根据beanDefinition 对象向IOC 容器中注入一个BasePackages 对象,这个对象的主要作用的提供给外部中间件,告诉他们需要扫描的路径。

@Import:自动配置最根本的注解,大部分@Enable 开头的注解下层基本都是这个注解。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    
    

@import 注解的四种用法

下面我们详谈一下@import 注解的四种用法

一:实现ImportSelector 接口

如果@Import 注解中标注的对象,也就是下面的AutoConfigurationImportSelector 对象,后面我们以A 对象简写,这个对象如果实现了ImportSelector 接口,spring 容器就会实例化A 对象,并调用其中重写的selectImports 方法。

二:实现DeferredImportSelector 接口

如果A 对象实现的是DeferredImportSelector 接口,这个接口是ImportSelector 子接口,实现它们两个的效果是一样的。

它们两个接口的区别在于两点:

  1. 执行时机,实现ImportSelector 接口的执行时机在@Configguration 注解中的其他逻辑被处理之前,也就是@Bean 之类的注解执行之前,而实现了DeferredImportSelector 接口自然是逻辑处理完成之后才能执行;
  2. DeferredImportSelector 的实现类可以用Order 注解,或者实现Ordered 接口来对selectImports 的执行顺序排序。

三:实现ImportBeanDefinitionRegistrar 接口

如果A 对象实现的是ImportBeanDefinitionRegistrar 接口,spring 容器同样会实例化A 对象,但是是调用它的重写接口的registerBeanDefinitions 方法。

四:没有上面三种接口

如果A 对象没有实现上面的三种接口,那么spring 容器就只会实例化A 对象。

@Import(AutoConfigurationImportSelector.class) 的作用

了解完@import 注解的用法之后,再说回来,我们继续看AutoConfigurationImportSelector 对象。

可以看到这里实现的是DeferredImportSelector 接口,同时实现了Ordered 接口用于排序,不过这里getOrder 的时候是最大值 - 1,优先级是最小的。

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
      ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
    
    

同时这个对象里面还有一个内部类,AutoConfigurationGroup 对象,后续run 方法走进来的时候,就是走这个类,具体的方法先后流程是process -> selectImports。

这里我们先看process 方法。这里重点看的就是getAutoConfigurationEntry 方法的调用,其余都是一些填充属性之类的,不重要。


public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
    
    
    Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
                 () -> String.format("Only %s implementations are supported, got %s",
                                     AutoConfigurationImportSelector.class.getSimpleName(),
                                     deferredImportSelector.getClass().getName()));
    AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
        .getAutoConfigurationEntry(annotationMetadata);
    this.autoConfigurationEntries.add(autoConfigurationEntry);
    for (String importClassName : autoConfigurationEntry.getConfigurations()) {
    
    
        this.entries.putIfAbsent(importClassName, annotationMetadata);
    }
}

获得所有自动配置类-getAutoConfigurationEntry方法

继续跟进getAutoConfigurationEntry 方法。下面代码中我已经写好注释了,我们重点要看的是三个地方:得到spring.factories 文件配置的所有自动配置类、剔除启动类上注解标明的不需要加载的配置类、剔除不满足配置类中条件注解修饰的配置类。

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    
    
   if (!isEnabled(annotationMetadata)) {
    
    
      return EMPTY_ENTRY;
   }
   AnnotationAttributes attributes = getAttributes(annotationMetadata);
   // 得到spring.factories 文件配置的所有自动配置类
   List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
   
   // 利用LinkedHashSet 移除重复的配置类
   configurations = removeDuplicates(configurations);
   
   // 得到要排除的自动配置类,比如注解属性exclude 的配置类
   // 比如:@springBootApplicaiton(exclude = FreeMarkerAutoConfiguration.class)
   // 将会获得exclude = FreeMarkerAutoConfiguration.class 注解数据
   Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    
   // 检查并剔除上面获得的,不需要自动配置的对象
   checkExcludedClasses(configurations, exclusions);
   configurations.removeAll(exclusions);
   
   // 剔除所有不符合条件的配置类,这里的条件是放在配置类对象上规定的,比如:@ConditionalOnProperty、@ConditionalOnClass
   // 因为太多的不必要对象会导致内存溢出
   configurations = getConfigurationClassFilter().filter(configurations);
   
   // 获取了符合条件的自动配置类后,此时触发AutoConfigurationImportEvent 事件
   fireAutoConfigurationImportEvents(configurations, exclusions);
    
   // 将符合条件和要排除的自动配置类封装进AutoConfigurationEntry 对象
   return new AutoConfigurationEntry(configurations, exclusions);
}

得到所有自动配置类-getCandidateConfigurations方法调用

我们直接跟进上面提到的getCandidateConfigurations 方法。

这里关注两点,getSpringFactoriesLoaderFactoryClass() 这个方法获取的是定值EnableAutoConfiguration.class,还有就是后面的getBeanClassLoader() 方法,这个方法是从META-INF/spring.factories 文件中去获取对应的值。

getBeanClassLoader() 方法具体源码可以不用看,具体作用就是根据EnableAutoConfiguration.class 去获取在spring.factories 文件中对应的值,而spring.factories 文件中的值存在是以键值对的形式存在的。

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    
    
   List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
         getBeanClassLoader());
   Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
         + "are using a custom packaging, make sure that file is correct.");
   return configurations;
}

image-20230524175627294

所以走完上述的getCandidateConfigurations 方法就能获取到所有META-INF/spring.factories 文件中EnableAutoConfiguration 对应的所有值,注意:这个文件只一个,这里项目中所有的这个文件。

排除不需要应用的自动配置类-getExclusions方法

接下来我们需要关注的就是getExclusions 方法,先说结论:这个方法调用获得是@springbootApplication 注解中exclude、excludeName 属性标注的类,用于排除上面getCandidateConfigurations 获取的所有自动配置类的部分不应用配置类。

试例如下:

@SpringBootApplication(exclude = OrderInfoController.class)

这个具体方法也很简单,获取对应的属性值就行,这里后将这些获取的所有类全部排除,后续也不会将它们加载为BeanDefinition。

protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    
    
   Set<String> excluded = new LinkedHashSet<>();
   excluded.addAll(asList(attributes, "exclude"));
   excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
   excluded.addAll(getExcludeAutoConfigurationsProperty());
   return excluded;
}

过滤出需要应用的核心自动配置类-getConfigurationClassFilter().filter方法

这个是最后一次过滤,会将上面得到的所有配置进行判断,判断的逻辑来源于各个配置类的@Conditional 之类的条件注解,具体点就是比如:@ConditionalOnClass@ConditionalOnBean 等等。

具体看下这个试例,被@ConditionalOnClass 注解修饰,如果IOC 容器中不存在DispatcherServlet.class,那么将会把被注解修饰的DispatcherServletAutoConfiguration 排除掉,不会在后续代码中生成对应的BeanDefinition,更不会生成对应的bean 存入IOC。

@ConditionalOnClass(DispatcherServlet.class)
public class DispatcherServletAutoConfiguration {
    
    }

spring 官网 对于条件注解的文档是全英文的,如果英文不错的可以自己去了解看看,但是也就可以参考一下我下面找到的一些翻译过后的文档,当然不会有官网详细。

image-20230525105158306

关于条件注解的讲解

@Conditional 是Spring4 新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册bean。

  • @ConditionalOnBean:仅仅在当前上下文中存在某个对象时,才会实例化一个Bean。
  • @ConditionalOnClass:某个class位于类路径上,才会实例化一个Bean。
  • @ConditionalOnExpression:当表达式为true的时候,才会实例化一个Bean。基于SpEL表达式的条件判断。
  • @ConditionalOnMissingBean:仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean。
  • @ConditionalOnMissingClass:某个class类路径上不存在的时候,才会实例化一个Bean。
  • @ConditionalOnNotWebApplication:不是web应用,才会实例化一个Bean。
  • @ConditionalOnWebApplication:当项目是一个Web项目时进行实例化。
  • @ConditionalOnNotWebApplication:当项目不是一个Web项目时进行实例化。
  • @ConditionalOnProperty:当指定的属性有指定的值时进行实例化。
  • @ConditionalOnJava:当JVM版本为指定的版本范围时触发实例化。
  • @ConditionalOnResource:当类路径下有指定的资源时触发实例化。
  • @ConditionalOnJndi:在JNDI存在的条件下触发实例化。
  • @ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者有多个但是指定了首选的Bean时触发实例化。

小结

上述基本已经讲完了getAutoConfigurationEntry 方法的最主要的三部分功能,其实也就是一个目的:获取所有的自动配置类的地址信息并进行封装保存。

AutoConfigurationImportSelector 对象中的其余内容其实可以不用看了,最主要的也就是process 方法,后续run 方法中我们再去看方法的调用时机。

run方法的调用流程

上述总的来说是对@SpringBootApplication 注解的解析,并没有说明这个是什么时候解析的,这就需要看在main 方法中调用的run 方法了。

一直跟进我们会到第一个重点SpringApplication 的创建,这里创建的时候会去构建一些springboot 的一些本身初始器,用于后续的流程,具体的介绍了。

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    
    
   return new SpringApplication(primarySources).run(args);
}

继续跟进run 方法,这个代码过多,我们先要记住我们的目的:自动配置流程,带着目的我们再看回代码,这样我们需要的关注点就不多了。

首先说下最开始的一些准备工作,getRunListeners(args); 用于创建监听器,然后new DefaultApplicationArguments(args) 创建上下文对象。

然后这个prepareEnvironment 方法的调用需要注意下,如果项目中配置了bootstrap 级别的配置文件,这里会发布一个事件给到BootstrapApplicationListener 对象,执行对应的事件,但是这里会回调到run 方法里面来,提前将配置信息存入容器中,当然如果项目中同时配置了application 级别的配置文件,并且与bootstrap 文件中有同一属性,后续配置会以application 文件为准。

public ConfigurableApplicationContext run(String... args) {
    
    
    //记录程序运行时间
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    // ConfigurableApplicationContext Spring 的上下文
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    //java.awt.headless是J2SE的一种模式用于在缺少显示屏、键盘或者鼠标时的系统配置,很多监控工具如jconsole 需要将该值设置为true,系统变量默认为true
    configureHeadlessProperty();
    //【1、获取并启动监听器】
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
    
    
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        //【2、准备环境】
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        //处理需要忽略的Bean
        configureIgnoreBeanInfo(environment);
        //打印banner
        Banner printedBanner = printBanner(environment);
        ///【3、初始化应用上下文】
        context = createApplicationContext();
        //实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                                                         new Class[] {
    
     ConfigurableApplicationContext.class }, 
                                                         context);
        //【4、刷新应用上下文前的准备阶段】
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        //【5、刷新应用上下文】
        refreshContext(context);
        //【6、刷新应用上下文后的扩展接口】
        afterRefresh(context, applicationArguments);
        //时间记录停止
        stopWatch.stop();
        if (this.logStartupInfo) {
    
    
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        //发布容器启动完成事件
        listeners.started(context);
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
    
    
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
    
    
        listeners.running(context);
    }
    catch (Throwable ex) {
    
    
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

创建上下文对象ServletWebServerApplicationContext对象-createApplicationContext方法

当上面run 方法回调结束之后,再继续往下走就到了createApplicationContext 方法,这里算是第一个重点。注意啊,回调没有结束之前是不会正在的创建出context 对象的。

这里创建context 对象是AnnotationConfigServletWebServerApplicationContext 对象,并且在构建对象的时候会构建出两个其余的对象。

public AnnotationConfigServletWebServerApplicationContext(DefaultListableBeanFactory beanFactory) {
    
    
   super(beanFactory);
   this.reader = new AnnotatedBeanDefinitionReader(this);
   this.scanner = new ClassPathBeanDefinitionScanner(this);
}

在构建AnnotatedBeanDefinitionReader 对象的时候,又会有AnnotationConfigUtils.registerAnnotationConfigProcessors 的调用,在这里会注册多个beanDefinition,其中有一个就是ConfigurationClassPostProcessor 对象,这是一个BeanFactoryPostProcessor 对象,是个重点,后续解析注解的时候会用到。

这里的重点也就是这个ConfigurationClassPostProcessor 对象的beanDefinition 对象构建并存入容器。

容器刷新及bean的构建-refreshContext方法

继续往下走,我们关注的第二个重点来了,如果看过spring 的源码,这个方法一定不陌生,bean 的构建和beanFactory 的构建还有spring 需要的一系列的东西都是这个方法来初始化的,这里就不属于springboot 的逻辑了,而是spring 的逻辑。

这个方法这里就不详细展示了,如果有兴趣可以去看我的spring 源码解析流程。

刚刚我们在context 对象创建的时候,发现了那里会附带的创建一个ConfigurationClassPostProcessor 对象,我们这里直接可以看到beanFactoryPostProcessor 的构建和执行,也就是invokeBeanFactoryPostProcessors 方法的调用。

这个方法里面会去获取到所有的beanFactoryPostProcessor 的beanDefinition,然后进行getBean,然后进行排序,最后根据顺序去执行beanFactoryPostProcessor 里面的postProcessBeanFactory。

ConfigurationClassPostProcessor 对象属于BeanDefinitionRegistryPostProcessor 接口的实现,所以这里我们需要执行的是它的postProcessBeanDefinitionRegistry 方法。

跟进这个方法,这里主要就是调用processConfigBeanDefinitions 方法,后续我们也是在这个方法里面摸爬滚打。

解析注解修饰的对象,生成对应的BeanDefinition-processConfigBeanDefinitions方法

这个方法的内容还是过多,我们不一步一步看了,主要看重点。

第一个重点parser.parse 的调用。这里我们主要看两点parse 方法的调用和deferredImportSelectorHandler.process() 的调用。

获取扫描路径,解析对应注解,得到BeanDefinition存入容器-parse方法

先看parse 方法的调用,这里需要继续跟进doProcessConfigurationClass 方法,可以看到,spring 里面真正做事还是do 开始的方法,比如getBean -> doGetBean -> createdBean -> doCreatedBean 的调用逻辑,做事的就是doGetBean 和doCreatedBean 两个方法。

下面代码中是我们需要看到的第一个地方,这里会获取到配置的@ComponentScan 注解标明的扫描路径,然后调用this.componentScanParser.parse 去将扫描路径下的所有被@Component 注解所修饰的对象,并生成对应的BeanDefinition 对象存入容器。

具体的代码流程:componentScanParser.parse -> scanner.doScan -> findCandidateComponents,可以自己跟debug 走一遍。

// Process any @ComponentScan annotations
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) {
    
    
        // The config class is annotated with @ComponentScan -> perform the scan immediately
        Set<BeanDefinitionHolder> scannedBeanDefinitions =
            this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
        // Check the set of scanned definitions for any further config classes and parse recursively if needed
        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());
            }
        }
    }
}

回到doProcessConfigurationClass 方法,继续往下走processImports 方法,这个方法主要就是处理@Impost 注解修饰的对象,这里会先处理调用实现ImportSelector 接口的对象的selectImports 方法,但是不会调用DeferredImportSelector 和ImportBeanDefinitionRegistrar 接口的实现类的对应方法,只会将这两者的实现类存入到deferredImportSelectorHandler 对象中。

后续doProcessConfigurationClass 方法中还有@Bean 等注解的解析,同样也会生成对应的BeanDefinition 后存入容器。

调用DeferredImportSelector接口实现类的process和ImportSelector方法-deferredImportSelectorHandler.process()

结束了doProcessConfigurationClass 方法,我们回到开始的地方parser.parse 调用地方,现在要看的是process 方法的调用,之前我们看@import 注解四种用法的时候,可以知道当ImportSelector 接口实现类的selectImports 方法调用完之后,就是轮到了它的子接口,也就是DeferredImportSelector 接口的process 和ImportSelector 方法调用了。

我们直接跟进代码,具体就写出来了,我直接说明代码流程process() -> processGroupImports() -> getImports(),到最后一步方法就可以直观的看到接口实现类的方法调用了。

这里就是会先调用process 方法,再调用selectImports 方法,也就说这里会调用到我们之前在看@SpringBootApplication 注解解析的时候涉及到逻辑了,换句说到这一步,我们能获得所有符合条件的自动配置类的全路径。

注意啊:这里是还没有将这些自动配置类生成对应的BeanDefinition 的。

public Iterable<Group.Entry> getImports() {
    
    
   for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
    
    
      this.group.process(deferredImport.getConfigurationClass().getMetadata(),
            deferredImport.getImportSelector());
   }
   return this.group.selectImports();
}

parser.parse 方法的调用到这里算是已经结束了,我们现在回到processConfigBeanDefinitions 方法中。

自动配置类生成对应的BeanDefinition存入容器-loadBeanDefinitions方法

我们现在看第二个重点,也是最后自动配置的最后一个重点,this.reader.loadBeanDefinitions(configClasses); 的调用执行。

还是老规矩跟进代码,代码流程:loadBeanDefinitions -> loadBeanDefinitionsForConfigurationClass

这里主要看我注释了的代码,一共是四个方法调用,有兴趣的可以点进去看下,也可以直接看我的注释,这里主要做的就是四件事情:

  1. 将上面得到的自动配置的全路径,解析封装为BeanDefinition 对象,并存入容器;
  2. 将自动配置类中被@Bean 注解修饰的对象,封装为BeanDefinition 对象,并存入容器;
  3. 将自动配置类中涉及的@Impost 注解修饰的对象,再走一遍刚刚的流程;
  4. 执行ImportBeanDefinitionRegistrar 接口实现类的registerBeanDefinitions 方法。
if (configClass.isImported()) {
    
    
      // 将所有的自动配置类生成对应的BeanDefinition 并存入容器
      registerBeanDefinitionForImportedConfigurationClass(configClass);
   }
   for (BeanMethod beanMethod : configClass.getBeanMethods()) {
    
    
      // 解析自动配置类中,被@Bean 注解修饰的方法,生成对应的BeanDefinition 并存入容器
      loadBeanDefinitionsForBeanMethod(beanMethod);
   }

   // 解析自动配置类中,被@Import 注解修饰的对象,生成对应的BeanDefinition 并存入容器
   loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
   // 调用ImportBeanDefinitionRegistrar 接口实现类的registerBeanDefinitions 方法
   loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

小结

到这里processConfigBeanDefinitions 方法的调用就不用看了,只要知道这里是将自动配置类生成了对应BeanDefinition 并存入了容器中就行。

后续的refresh 方法中会经过finishBeanFactoryInitialization 方法,最后将这些BeanDefinition 进行实例化和初始化后,生成对应的bean 对象,存入IOC 的单例池中即可,到此自动配置的全流程就结束了。

总结

spring boot 本身就是对spring 的封装,所以很逻辑都是通用的spring 的,spring boot 本身自己的逻辑也可看做是对spring 的一个加强补充,所以spring boot 本身也是非常容易上手的。

再说spring boot 的自动配置的源码流程,说白了,就通过spring 中原本的BeanFactoryPostProsessor 机制来实现自动配置全路径转换为BeanDefinition 的过程,后续的还是生成Bean 的流程还是Spring 的那套getBean -> doGetBean- > createdBean -> doCreatedBean 那套。

至于集成Tomcat 和启动时配置集成的spring MVC 相关的东西,后续写Tomcat 和MVC 的时候再说。

猜你喜欢

转载自blog.csdn.net/qq_39339965/article/details/130951869
今日推荐