首页 > 编程学习 > 【SpringBoot源码】源码分析

【SpringBoot源码】源码分析

发布时间:2022/11/6 2:36:04

【SpringBoot源码】源码分析

  • (1)springboot是什么
  • (2)springboot的启动流程
    • (1)springboot启动的整体流程图
    • (2)springboot源码分析过程
      • (1)运行 SpringApplication.run() 方法
        • (1)构造器SpringApplication方法
          • (1)找到springboot的启动类的启动方法run,进行dubug
          • (2)进入启动类SpringApplication里的run方法
          • (3)再点进去看到run方法的主体,在这里的run方法里new了一个启动类SpringApplication对象
          • (4)点进启动类的构造方法SpringApplication中去看,这个方法里的内容很重要
          • (5)设置程序运行的主类
          • (6)看完构造方法SpringApplication后
        • (2)run方法
          • (1)run方法整体一览
          • (2)开启计时器
          • (3)准备异常报告信息存放的ArrayList
          • (4)将java.awt.headless设置为true
          • (5)获取并启用监听器
          • (6)设置应用程序参数
          • (7)准备环境变量
          • (8)配置忽略bean信息
          • (9)打印banner信息
          • (10)创建应用程序的上下文
          • (11)实例化异常报告器
          • (12)prepareContext方法:往上下文对象中设置一系列的属性值
            • (1) 设置环境的信息
            • (2)设置转换服务
            • (3)把当前的context进行一些初始化
            • (4)发布上下文已经准备好了的事件
            • (5)通过上下文获取bean工厂(自动装配的重要方法!!!)
          • (13)refreshContext方法:刷新上下文
            • (1)自动装配
            • (2)Tomcat内嵌
          • (14)刷新上下文后置处理
          • (15)结束计时器
          • (16)发布上下文准备就绪事件
          • (17)执行自定义的run方法
  • (3)springboot的自动装配
    • (1)自动装配的本质
    • (2)spring自动装配的原理
      • (1)启动类上注解的作用
    • (3)springboot自动装配的流程
    • (4)自动装配源码分析
  • (4)springboot的Tomcat内嵌
    • (1)

(1)springboot是什么

springboot是依赖于spring的,比起spring,除了拥有spring的全部功能以外,springboot没有繁琐的xml配置,这取决于自身强大的自动装配功能;并且自身已嵌入Tomcat
Jetty等web容器,集成了springmvc,使得springboot可以直接运行,不需要额外的容器,提供了一些大型项目中常见的非功能性特性,如嵌入式服务器、安全、指标、健康监测、外部配置等等。

boot也就是启动的意思,所以springboot其实就是一个启动spring项目的一个工具而已,总而言之,springboot是一个服务于框架的框架;也可以说springboot是一个工具,这个工具简化了spring的配置。

那么springboot源码主要看的就是三点:
1-springboot的启动流程
2-自动装配
3-Tomcat内嵌

(2)springboot的启动流程

(1)springboot启动的整体流程图

在这里插入图片描述

在这里插入图片描述

(2)springboot源码分析过程

(1)运行 SpringApplication.run() 方法

(1)构造器SpringApplication方法

(1)找到springboot的启动类的启动方法run,进行dubug

在这里插入图片描述

(2)进入启动类SpringApplication里的run方法

在这里插入图片描述

(3)再点进去看到run方法的主体,在这里的run方法里new了一个启动类SpringApplication对象

在这里插入图片描述

(4)点进启动类的构造方法SpringApplication中去看,这个方法里的内容很重要

1)首先会通过 WebApplicationType.deduceFromClasspath(); 方法判断当前应用程序的容器,默认使用的是Servlet 容器,除了servlet之外,还有NONE 和 REACTIVE (响应式编程);
在这里插入图片描述

2)其中最重要的两个方法:
1-setInitializers:设置初始化
2-setListeners:设置监听器
这两个方法的主体都是一样的,只是传的参数不一样,说明这个方法一定是抽象的
在这里插入图片描述
3)先看第一行的,传的参数是ApplicationContextInitializer类,点进去
看到loadFactoryNames方法
在这里插入图片描述
再点进去,在这里load的时候会到配置文件spring.factories里去加载信息,在这个文件中指定了一些类的完全限定名,也就是包名和类名
在这里插入图片描述
进入这个方法,看到这个静态变量FACTORIES_RESOURCE_LOCATION
在这里插入图片描述

点进去静态变量可以看到指定的文件是spring.factories
在这里插入图片描述
找到这个配置文件,可以看到这个文件里存放的都是包名和类型,在执行的时候就要把这些符合规则的类从配置文件里读取出来
在这里插入图片描述
还有另外一个配置文件的作用相同

我们也可以自己实现一个自定义的初始化器:实现 ApplicationContextInitializer接口既可

4)分析一下最终取出来的类的个数
可以看到debug结果显示从配置文件里取出来13个类
在这里插入图片描述
找到启动类,当时传了两个类名进来,分别是ApplicationContextInitializer和ApplicationListener,然后点开配置文件进行查找,可以看到标识符下面有的类名的个数
在这里插入图片描述
ApplicationContextInitializer在两个文件中对应的分别有5个和2个,共有7个。实现的是ApplicationContextInitializer接口。
在这里插入图片描述
在这里插入图片描述

ApplicationListener在两个文件中对应的分别有10个和1个,共有11个。实现的是ApplicationListener接口。
在这里插入图片描述
在这里插入图片描述
综上,这里会根据方法中传的类名到配置文件里获取对应的所有类名,也就是把所有的初始化器的类(7个)和监听器的类(11个)都取到,这里取出来的只是类的完全限定名,并不是一个对象,有了包名+类名后面就可以通过反射Class.forName来对这些类进行实例化获取这些类的对象

5)取出类名后的实例化操作
把loader方法取出来的names放进一个Set,然后放进下面这个方法进行实例化
在这里插入图片描述
在方法中对names进行遍历,然后通过forName获取类和构造器constructor等,接着通过调用instantiate方法进行实例化对象
在这里插入图片描述
点进instantiate方法可以看到关键点代码,在这里返回具体的实例化对象
在这里插入图片描述

最后的实例化对象放进list中被返回

(5)设置程序运行的主类

deduceMainApplicationClass(); 这个方法仅仅是找到main方法所在的类,为后面的扫包作准备,deduce是推断的意思,所以准确地说,这个方法作用是推断出主方法所在的类;
在这里插入图片描述

(6)看完构造方法SpringApplication后

开始看看紧随其后的run方法,这里才是启动方法的主体

在这里插入图片描述

(2)run方法

(1)run方法整体一览
/**
 * Run the Spring application, creating and refreshing a new
 * {@link ApplicationContext}.
 * @param args the application arguments (usually passed from a Java main method)
 * @return a running {@link ApplicationContext}
 */
public ConfigurableApplicationContext run(String... args) {
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
   configureHeadlessProperty();
   SpringApplicationRunListeners listeners = getRunListeners(args);
   listeners.starting();
   try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
      configureIgnoreBeanInfo(environment);
      Banner printedBanner = printBanner(environment);
      context = createApplicationContext();
      exceptionReporters = getSpringFactoriesInstances(
            SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
      prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
      refreshContext(context);
      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;
}
(2)开启计时器

第一步调用的run方法时静态方法,那个时候还没有实例化SpringApplication对象,现在调用的run方法是非静态的,是需要实例化后才可以调用的,进来会首先开启计时器,计算springboot启动一共花了多长时间

// 实例化计时器
StopWatch stopWatch = new StopWatch(); 
// 开始计时
stopWatch.start();

在这里插入图片描述
start方法实体
在这里插入图片描述
那么下面也肯定会有一个停止计时的方法stop
在这里插入图片描述

(3)准备异常报告信息存放的ArrayList

当启动的过程产生异常信息的时候就存放到这个集合里面
在这里插入图片描述

(4)将java.awt.headless设置为true

这里将java.awt.headless设置为true,表示运行在服务器端,在没有显示器器和鼠标键盘的模式下照样可以工作,模拟输入输出设备功能。
在这里插入图片描述

做了这样的操作后,SpringBoot想干什么呢?其实是想设置该应用程序,即使没有检测到显示器,也允许其启动,对于服务器来说,是不需要显示器的,所以要这样设置。

private void configureHeadlessProperty() {
	System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(
			SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}

通过方法可以看到,setProperty()方法里面又有个getProperty();这不多此一举吗?其实getProperty()方法里面有2个参数, 第一个key值,第二个是默认值,意思是通过key值查找属性值,如果属性值为空,则返回默认值 true;保证了一定有值的情况;

(5)获取并启用监听器

这一步 通过监听器来实现初始化的的基本操作,这一步做了2件事情
1-创建所有spring运行监听器并发布应用启动事件
2-启用监听器
在这里插入图片描述
方法点进去,在这个方法里跟之前一样也是从配置文件里获取包名+类名
在这里插入图片描述
对应文件在配置文件spring.factories里的位置
在这里插入图片描述
这个类就是用来对事件的整体发布

starting这里发布事件,如果有监听器正在监听,那么就进行处理

(6)设置应用程序参数

将执行run方法时传入的参数args封装成一个对象DefaultApplicationArguments的对象
在这里插入图片描述
仅仅是将参数封装成对象,没啥好说的,对象的构造函数如下

public DefaultApplicationArguments(String[] args) {
	Assert.notNull(args, "Args must not be null");
	this.source = new Source(args);
	this.args = args;
}

那么问题来了,这个参数是从哪来的呢?其实就是main方法里面执行静态run方法传入的参数,

在这里插入图片描述

(7)准备环境变量

准备环境变量,包含系统属性和用户配置的属性(包含JDK和库等信息),执行的代码块在 prepareEnvironment 方法内
在这里插入图片描述
prepareEnvironment方法内部
在这里插入图片描述
打了断点之后可以看到,它将maven和系统的环境变量都加载进来了

在这里插入图片描述
如果后面程序中有用到这些信息的话,就直接从环境变量中来获取,不用重新去读取了

(8)配置忽略bean信息

执行属性包含的对象bean都给忽略掉
在这里插入图片描述

这个方法configureIgnoreBeanInfo() 这个方法是将 spring.beaninfo.ignore 的默认值值设为true,意思是跳过beanInfo的搜索,其设置默认值的原理和第7步一样;

private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
	if (System.getProperty(
			CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
		Boolean ignore = environment.getProperty("spring.beaninfo.ignore",
				Boolean.class, Boolean.TRUE);
		System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME,
				ignore.toString());
	}
}

当然也可以在配置文件中添加以下配置来设为false

spring.beaninfo.ignore=false
(9)打印banner信息

在这里插入图片描述

显而易见,这个流程就是用来打印控制台那个很大的spring的banner的
在这里插入图片描述
那他在哪里打印的呢?他在 SpringBootBanner.java 里面打印的,这个类实现了Banner 接口,而且banner信息是直接在代码里面写死的;
在这里插入图片描述
有些公司喜欢自定义banner信息,如果想要改成自己喜欢的图标该怎么办呢,其实很简单,只需要在resources目录下添加一个 banner.txt 的文件即可,txt文件内容如下

                 _           _
                (_)         | |
 _   _  _____  ___ _ __   __| | ___  _ __   __ _
| | | |/ _ \ \/ / | '_ \ / _` |/ _ \| '_ \ / _` |
| |_| |  __/>  <| | | | | (_| | (_) | | | | (_| |
 \__, |\___/_/\_\_|_| |_|\__,_|\___/|_| |_|\__, |
  __/ |                                     __/ |
 |___/                                     |___/
:: yexindong::

一定要添加到resources目录下,别加错了
在这里插入图片描述

只需要加一个文件即可,其他什么都不用做,然后直接启动springboot,就可以看到效果了
在这里插入图片描述

(10)创建应用程序的上下文

实例化应用程序的上下文, 调用 createApplicationContext() 方法,这里就是用反射创建对象
在这里插入图片描述

ApplicationContext和BeanFactory之间的关系:
ApplicationContext继承自BeanFactory
在这里插入图片描述

(11)实例化异常报告器

异常报告器是用来捕捉全局异常使用的,当springboot应用程序在发生异常时,异常报告器会将其捕捉并做相应处理,在spring.factories 文件里配置了默认的异常报告器,
在这里插入图片描述
需要注意的是,这个异常报告器只会捕获启动过程抛出的异常,如果是在启动完成后,在用户请求时报错,异常报告器不会捕获请求中出现的异常,
在这里插入图片描述
了解原理了,接下来我们自己配置一个异常报告器来玩玩;

MyExceptionReporter.java 继承 SpringBootExceptionReporter 接口

package com.spring.application;
 
import org.springframework.boot.SpringBootExceptionReporter;
import org.springframework.context.ConfigurableApplicationContext;
 
public class MyExceptionReporter implements SpringBootExceptionReporter {
 
 
    private ConfigurableApplicationContext context;
    // 必须要有一个有参的构造函数,否则启动会报错
    MyExceptionReporter(ConfigurableApplicationContext context) {
        this.context = context;
    }
 
    @Override
    public boolean reportException(Throwable failure) {
        System.out.println("进入异常报告器");
        failure.printStackTrace();
        // 返回false会打印详细springboot错误信息,返回true则只打印异常信息 
        return false;
    }
}

在 spring.factories 文件中注册异常报告器

# Error Reporters 异常报告器
org.springframework.boot.SpringBootExceptionReporter=\
com.spring.application.MyExceptionReporter

在这里插入图片描述
接着我们在application.yml 中 把端口号设置为一个很大的值,这样肯定会报错,

server:
  port: 80828888

启动后,控制台打印如下图
在这里插入图片描述

(12)prepareContext方法:往上下文对象中设置一系列的属性值

上面的代码已经准备好了context对象,接下来就是该往上下文对象里设置一系列的属性值。

自动装配的准备工作实际上就是在BeanFactoryPostProcesser里进行拓展实现的,在springboot启动流程中的位置就是在prepareContext方法。

在run方法中,prepareContext方法之前的所有方法主要完成的工作就是进行准备工作,进行context的属性赋值和事件发布,让各种监听器能够监听到对应的事件
在这里插入图片描述

(1) 设置环境的信息

一些操作系统的环境、JDK环境、Maven信息,文件路径等等环境的信息在这里完成赋值。
在这里插入图片描述

(2)设置转换服务

设置类型转换器
在这里插入图片描述

后续需要的一些转换服务在这里先设置好,例如123转换成整数
在这里插入图片描述

(3)把当前的context进行一些初始化

进行一些初始化赋值
在这里插入图片描述

(4)发布上下文已经准备好了的事件

在这里插入图片描述
发布这个事件后,看哪个监听器能接收这个事件,如果能接收到就进行处理,接收不到就跳过不处理
在这里插入图片描述

(5)通过上下文获取bean工厂(自动装配的重要方法!!!)

在spring框架中说过,beanFactory实际使用的是子类DefaultListableBeanFactory,在这里获取的也是这个工厂类,有了工厂就可以创建bean对象了
在这里插入图片描述

方法点进去,看下图,前面的部分是设置属性值,后面的load方法很重要
在这里插入图片描述
上面有个getAllSources方法,这个方法返回的结果source就是一个Set集合,集合中只有一个数据就是SpringbootDataApplication,也就是上下文,这个结果会在下面的load方法中当做参数来使用
在这里插入图片描述
点进load方法看,方法中先创建BeanDefinitionReader,获取到bean的定义信息
在这里插入图片描述
依然是在这个load方法里,最后实际干活的地方是这个bean定义信息加载器调用的load方法
在这里插入图片描述

点进这个load方法,可以看到对source上下文的遍历处理,上面已经知道了source这个set里面只有一个数据SpringbootDataApplication,也就是我们的启动类,所以这里其实也就是只循环了一次,返回的结果count也自然就是1
在这里插入图片描述

上面在循环遍历的时候也调用了load(source)方法,点进去这个方法看看,在这个方法里主要是对source类型进行一个判断,根据类型再调用一个对应的load方法,这里的source也就是SpringbootDataApplication是Class类型的,所以会进入第一个判断力的load方法
在这里插入图片描述

再点进去这个load方法,刚开始有个判断,我们的文件的Java的,所以第一个判断进不去。然后第二个判断就是开始用isComponent方法判断这个参数启动类上是否有@Component注解,这个方法很重要。在spring等框架中会加一些注解,例如:@Controller、@Component等,那么这些注解是怎么被扫描到的呢?其实就是在这个isComponent方法中处理的
在这里插入图片描述
点进去isComponent方法,这个方法用来判断启动类是否有@Component注解,方法内部判断是否有这个注解,有的话才能返回true
在这里插入图片描述
那么我们就来看一下启动类上到底有没有@Component这个注解呢?这个方法的返回值到底会不会是true呢?前提是我们要知道有些注解时复合注解,找到启动类的位置以及上面的注解,然后点进去看一下
在这里插入图片描述
点进去看到很多注解,上面4个是元注解,下面这个@SpringBootConfiguration注解点进去
在这里插入图片描述
进来之后再点@Configuration注解
在这里插入图片描述
点进来之后就能看到了@Component这个注解,那么到这里就可以确定了,前面说的isComponent方法判断启动类SpringbootDataApplication上有没有@Component注解,结果看到是有的,所以确定这里的isComponent返回值是true
在这里插入图片描述

当返回值为true的时候,就可以通过register进行注册,然后通过这个注解读取器annotatedReader来读取对应启动类bean的信息,也就是读取启动类SpringbootDataApplication的信息
在这里插入图片描述
读取完成之后最后再发布一个监听器,到这里就完成了启动类的加载,为下一步里自动装配的实际行动做好准备工作。

(13)refreshContext方法:刷新上下文

刷新上下文已经是spring的范畴了,自动装配和启动 tomcat就是在这个方法里面完成的。在这里和spring框架做一个整合,类似于spring的refresh方法,点进去往下看能找到refresh方法
在这里插入图片描述
大部分内容和spring框架的refresh方法相同,主要不同体现在“自动装配”和“Tomcat内嵌”这两部分

(1)自动装配

(1)进入prepareContext方法
什么叫“准备上下文”,上下文就是存储一定时间范围内的数据,用到数据的时候就可以直接从上下文中获取
1-上一个方法说到自动转配的扩展是通过BeanFactoryPostProcessor实现的,那么这里就是在调用这个方法
在这里插入图片描述
点进去这个方法,可以看到一个getBeanFactoryPostProcessors方法,这个方法可以获取需要用到的BeanFactoryPostProcessor
在这里插入图片描述
点进去getBeanFactoryPostProcessors方法,这个方法会返回一个包含3个BeanFactoryPostProcessor的集合
在这里插入图片描述
所以完成getBeanFactoryPostProcessors方法就可以获得含有BeanFactoryPostProcessor的结合,那么下一步就是处理和使用这些BeanFactoryPostProcessor了
在这里插入图片描述
点进去这个方法…

(2)Tomcat内嵌
(14)刷新上下文后置处理

afterRefresh 方法是启动后的一些处理,留给用户扩展使用,目前这个方法里面是空的,

/**
	 * Called after the context has been refreshed.
	 * @param context the application context
	 * @param args the application arguments
	 */
protected void afterRefresh(ConfigurableApplicationContext context,
	ApplicationArguments args) {
}
(15)结束计时器

到这一步,springboot其实就已经完成了,计时器会打印启动springboot的时长
在这里插入图片描述

(16)发布上下文准备就绪事件

告诉应用程序,我已经准备好了,可以开始工作了
在这里插入图片描述

(17)执行自定义的run方法

这是一个扩展功能,callRunners(context, applicationArguments) 可以在启动完成后执行自定义的run方法;有2中方式可以实现:
1-实现 ApplicationRunner 接口
2-实现 CommandLineRunner 接口

验证一把

package com.spring.init;
 
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
 
/**
 * 自定义run方法的2种方式
 */
@Component
public class MyRunner implements ApplicationRunner, CommandLineRunner {
 
    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println(" 我是自定义的run方法1,实现 ApplicationRunner 接口既可运行"        );
    }
 
    @Override
    public void run(String... args) throws Exception {
        System.out.println(" 我是自定义的run方法2,实现 CommandLineRunner 接口既可运行"        );
    }
}

启动springboot后就可以看到控制台打印的信息了
在这里插入图片描述

(3)springboot的自动装配

(1)自动装配的本质

SpringBoot的自动装配是指:SpringBoot会自动将一些配置类的bean注册到ioc容器,我们可以在需要的地方使用@Autowired或@Resource等注解来使用它。自动的表现形式就是我们只需要引我们享用功能的包,其他的配置完全不需要管,springboot会自动注入这些配置备案,我们直接使用就行。

SpringBoot自动装配的本质就是通过Spring去读取META-INF/spring.factories中保存的配置类文件然后加载bean定义的过程,并生成相应的Bean对象,然后将它们交给spring容器来帮我们进行管理

(2)spring自动装配的原理

(1)启动类上注解的作用

(1)@SpringBootApplication
这个注解是springboot启动类上的一个注解,是一个组合注解,也就是由其他注解组合起来,它的主要作用就是标记说明这个类是springboot的主配置类,springboot应该运行这个类里面的main()方法来启动程序
在这里插入图片描述
这个注解主要由三个子注解组成:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
其他注解是元注解
在这里插入图片描述
(2)@SpringBootConfiguration
这个注解包含了@Configuration,@Configuration里面又包含了一个@Component注解,也就是说,这个注解标注在哪个类上,就表示当前这个类是一个配置类,而配置类也是spring容器中的组件
在这里插入图片描述
在这里插入图片描述
(3)@EnableAutoConfiguration:自动装配
这个注解是开启自动配置的功能,里面包含了两个注解
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
在这里插入图片描述
(4)@AutoConfigurationPackage
这个注解的作用说白了就是将主配置类(@SpringBootApplication标注的类)所在包以及子包里面的所有组件扫描并加载到spring的容器中,这也就是为什么我们在利用springboot进行开发的时候,无论是Controller还是Service的路径都是与主配置类同级或者次级的原因

(5)@Import(AutoConfigurationImportSelector.class)
上一个注解我们把所有组件都加载到了容器里面,这个注解就是将需要自动装配的类以全类名的方式返回,那是怎么找到哪些是需要自动装配的类呢?

1-AutoConfigurationImportSelector这个类里面有一个方法selectImports(),如下
在这里插入图片描述
2-在selectImport()方法里调用了一个getAutoConfigurationEntry()方法,这个方法里面又调用了一个getCandidateConfigurations()方法
在这里插入图片描述
3-在getCandidateConfigurations()方法里面调用了loadFactoryNames()方法
在这里插入图片描述
4-loadFactoryNames()方法里面又调用了一个loadSpringFactories()方法
在这里插入图片描述
5-关键就在这个loadSpringFactories()方法里面,在这个方法里,它会查找所有在META-INF路径下的spring.factories文件
在这里插入图片描述
在这里插入图片描述
6-在META-INF/spring.factories这个文件里面的数据是以键=值的方式存储,然后解析这些文件,找出以EnableAutoConfiguration为键的所有值,以列表的方式返回
在这里插入图片描述
在这里插入图片描述
(6)@ComponentScan
这个注解的作用就是扫描当前包及子包的注解

(3)springboot自动装配的流程

1、在springboot启动的时候会创建一个SpringApplication对象,在对象的构造方法里面会进行一些参数的初始化工作,最主要的是判断当前应用程序的类型以及设置初始化器以及监听器,并在这个过程中会加载整个应用程序的spring.factories文件,将文件中的内容放到缓存当中,方便后续获取;

2、SpringApplication对象创建完成之后会执行run()方法来完成整个应用程序的启动,启动的过程中有两个最主要的方法prepareContext()和refreshContext(),在这两个方法中完成了自动装配的核心功能,在run()方法里还执行了一些包括上下文对象的创建,打印banner图,异常报告期的准备等各个准备工作,方便后续进行调用;

3、在prepareContext()中主要完成的是对上下文对象的初始化操作,包括属性的设置,比如设置环境变量。在整个过程中有一个load()方法,它主要是完成一件事,那就是将启动类作为一个beanDefinition注册到registry,方便后续在进行BeanFactoryPostProcessor调用执行的时候,可以找到对应执行的主类,来完成对@SpringBootApplication、@EnableAutoConfiguration等注解的解析工作;

4、在refreshContext()方法中会进行整个容器的刷新过程,会调用spring中的refresh()方法,refresh()方法中有13个非常关键的方法,来完成整个应用程序的启动。而在自动装配过程中,会调用的关键的一个方法就是invokeBeanFactoryPostProcessors()方法,在这个方法中主要是对ConfigurationClassPostProcessor类的处理,这个类是BFPP(BeanFactoryPostProcessor)的子类,因为实现了BDRPP(BeanDefinitionRegistryPostProcessor)接口,在调用的时候会先调用BDRPP中的postProcessBeanDefinitionRegistry()方法,然后再调用BFPP中的postProcessBeanFactory()方法,在执行postProcessBeanDefinitionRegistry()方法的时候会解析处理各种的注解,包含@PropertySource、@ComponentScan、@Bean、@Import等注解,最主要的是对@Import注解的解析;

5、在解析@Import注解的时候,会有一个getImport()方法,从主类开始递归解析注解,把所有包含@Import的注解都解析到,然后在processImport()方法中对import的类进行分类,例如AutoConfigurationImportSelect归属于ImportSelect的子类,在后续的过程中会调用DeferredImportSelectorHandler类里面的process方法,来完成整个EnableAutoConfiguration的加载。

(4)自动装配源码分析

(1)在prepareContext方法里准备
在这里插入图片描述

(4)springboot的Tomcat内嵌

(1)

Copyright © 2010-2022 dgrt.cn 版权所有 |关于我们| 联系方式