SpringBoot解析

62 阅读9分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情

作为一个从业两年的小鸟,我没想到有一天我居然会去剖析SPringBoot源码,但是作为一个上进的小鸟,我觉得我应该去深入的了解一下,

首先,什么是SpringBoot

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。

SpringBoot最明显的特点是,让文件配置变的相当简单、让应用部署变的简单(SpringBoot内置服务器,并装备启动类代码),可以快速开启一个Web容器进行开发。

Spring Boot的核心功能

1、 可独立运行的Spring项目:Spring Boot可以以jar包的形式独立运行。

2、 内嵌的Servlet容器:Spring Boot可以选择内嵌Tomcat、Jetty或者Undertow,无须以war包形式部署项目。

3、 简化的Maven配置:Spring提供推荐的基础 POM 文件来简化Maven 配置。

4、 自动配置Spring:Spring Boot会根据项目依赖来自动配置Spring 框架,极大地减少项目要使用的配置。

5、 提供生产就绪型功能:提供可以直接在生产环境中使用的功能,如性能指标、应用信息和应用健康检查。

6、 无代码生成和xml配置:Spring Boot不生成代码。完全不需要任何xml配置即可实现Spring的所有配置。

一、依赖管理

我们都知道在创建完springboot项目之后pom都得继承一下springboot的parent,那我们就点进去看看这个parent里都有些啥

image.png

image.png 在这里我们发现他定义了一些配置文件默认规则以及jdk版本等,而且发现他又继承了dependencies,我们继续进入dependencies中

image.png

进来之后发现,里面定义了一堆的版本信息以及一些和当前springboot相匹配的坐标依赖,所以这时候我们在自己的项目中引用这些坐标的时候是不需要添加版本的,这些版本默认就是用这里定义的版本

上面解决了一个版本问题,那我们使用springboot的时候为什么就引入了一个starter一堆的jar包都会跟着到咱们的项目里了呢?我们不妨弄一个看看具体怎么回事

点进去发现这里面依赖了一些与web相关的坐标,这也就是为什么你在自己项目里引入了一个starter会自动给你关联出一堆坐标出来的原因

二、自动装配

接下来我们看一下springboot是如何做到自动装配的

image.png

我们都知道springboot项目都需要在启动类上加上一个org.springframework.boot.autoconfigure.SpringBootApplication注解,那这个注解的作用是啥呢?我们点进去看看

image.png

从上图我们可以看到这是一个组合注解,而主要的注解有3个,接下来我们分别看看这几个注解都是做啥的

进入org.springframework.boot.SpringBootConfiguration

image.png

可以看出,这个注解其实相当于就是一个org.springframework.context.annotation.Configuration注解的替身,主要就是用来向spring表明自己是一个配置类的

接下来看看第二个注解org.springframework.boot.autoconfigure.EnableAutoConfiguration里又做了啥

image.png

可以看到这里主要面有一个注册bean定义的方法,主要就是把被org.springframework.boot.autoconfigure.SpringBootApplication注解标记的包的定义信息注册到容器中中 。

回到org.springframework.boot.autoconfigure.EnableAutoConfiguration中

\

\

我们继续看引入的org.springframework.boot.autoconfigure.AutoConfigurationImportSelector里面做了啥

\

\

可以看到这里主要做了两件事1、封装自动配置元数据信息;2、封装自动配置的信息

接下来我们继续跟进org.springframework.boot.autoconfigure.AutoConfigurationMetadataLoader#loadMetadata方法

\

\

可以看到这里主要就是去加载spring-autoconfigure-metadata.properties文件中的信息进行封装返回,那这个文件里都定义了啥呢

\

\

可以看到这里主要就是定义了一些需要自动配置的类自动装配需要满足的一些条件

回到org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#selectImports中继续往下看

\

\

进入org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry方法中

\

\

可以看到这里主要做了三件事1、加载需要自动配置的类信息;2、根据条件筛选自动配置类信息;3、发布事件

接下来我们进入org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getCandidateConfigurations看看

\

\

继续跟进

\

\

发现这里的套路和之前一样,都是去加载一个配置文件中的内容,接下来我们去看看spring.factories这里面都有啥

\

\

\

\

可以看到这里面定义了一堆需要自动装配的bean,而这些ben里面都会有一个或多个条件,也就是说他要装配这个bean,需要满足已经达成这些条件才进行自动装配

所以到这里自动装配的原理咱们也就搞明白了

接下来我们回到org.springframework.boot.autoconfigure.SpringBootApplication这个注解中,看看最后一个注解org.springframework.context.annotation.ComponentScan

\

\

ComponentScan这个注解熟悉spring的同学应该都知道,他的作用就是去扫描指定包以及子包中的组件,但是又有同学问了,咱们也没配置哪个包啊他是怎么去扫描的呢?

其实啊,在刚刚咱们再看自动装配源码的时候不是已经拿到了一个包吗?没错,就是被org.springframework.boot.autoconfigure.SpringBootApplication标记的类所在的包路径

好了到这里我们就把org.springframework.boot.autoconfigure.SpringBootApplication这个注解所做的事情看完了咱们可以大致总结一下这个注解都起到了什么作用

1、获取被org.springframework.boot.autoconfigure.SpringBootApplication注解标记的类的包路径

2、获取并装配需要自动装配的类信息

3、扫描1获取到的包包路径下的组件信息

接下来我们看看程序启动的流程是怎样的的

\

\

首先进入咱们的启动类进入org.springframework.boot.SpringApplication#run方法中

\

\

继续跟进

\

\

看可以可看到这里主要做两件事1、创景springbootApplication实例;2、调用run方法

接下来我们看看springbootApplication的 构造方法里都做了什么

\

\

可以看到这里主要做了5件事,都是在初始化自身的一些属性我们继续看看都具体租了啥

  1. 初始化主程序类信息
  2. 获取web应用类型

\

\

由于这里我们使用的是springmvc作为前端控制器,所以这里反悔的是servlet类型

3.设置初始化

4.设置监听器类型

\

\

我们可以看到3和4调用的都是一个方法,只是传入的类型不一样而已,接下来我们看看这个方法里兜做了啥

\

\

嗯?这不是之前看自动装配的时候用到的那个方法吗,他的作用就是去META-INF/spring.factories文件中找自动装配的类信息啊,接下来我们看看这个文件中是否有这两个类的内容

\

\

果然有啊,接下来我们回到org.springframework.boot.SpringApplication#getSpringFactoriesInstances方法中继续往下看

\

\

哦,这里直接拿到这些类之后就创建对一个的实例进行返回

我们回到org.springframework.boot.SpringApplication#SpringApplication中继续看构造方法的最后一步

5.设置程序入口类实例

\

\

哦,这里主要就是推断main方法所在的类的,然后进行实例化返回

好了,构建springbootApplication对象到这里基本就完事了,接下来我们再看看run方法里又做了啥

\

\

\

我们可以看到这里主要就做了7件事接下来我们具体看看

1、创建并处理监听器状态

\

\

这里我们就不过多少说,因为之前我们已经看过多遍这种代码了

我们直接进入org.springframework.boot.SpringApplicationRunListeners中看看这里面都做啥了

\

\

嗯?这好像是一个发布订阅的模式哦

回到run方法我们继续看

2、准备运行环境方法进入org.springframework.boot.SpringApplication#prepareEnvironment

\

\

这里就构建了一个运行环境并绑定到当前springbootapplication

回到run方法

3、创建上下文对象

进入org.springframework.boot.SpringApplication#createApplicationContext方法

\

\

\

这里就创建了一个spring的实例并返回

继续回到run方法

4、上下文前置处理

进入org.springframework.boot.SpringApplication#prepareContext方法

\

\

\

可以看到这里主要就是把当前上下文添加到spring容器中

回到run方法中

5、刷新容器

进入org.springframework.boot.SpringApplication#refreshContext方法中

\

\

继续进入org.springframework.boot.SpringApplication#refresh中

\

\

嗯?这好像是spring容器的刷新方法啊,是不是呢,我们点进去看看

进入org.springframework.context.support.AbstractApplicationContext#refresh方法中

\

\

哎,可不就是spring上下文刷新方法吗,所以我们知道这一步主要就是初始化spring上下文

回到run方法中

6、上下文后置处理

进入org.springframework.boot.SpringApplication#afterRefresh方法中

\

\

哦,这里啥也没做,就是一个模板方法,可能使用于扩展性的吧

回到run方法

进入org.springframework.boot.SpringApplication#callRunners方法中

\

\

\

可以看到这里主要就是从容器中获取runner对象并调用对应的run方法

好了,到这里sping的启动流程也就说完了

接下来我们总结一下springboot都做了些什么

1、依赖管理:

  1. 版本管理:所有的springboot项目都得继承spring-boot-starter-parent,而spring-boot-starter-parent继承了spring-boot-dependencies,而在spring-boot-dependencies中定义了一些版本信息,所以我们在引入一些依赖的时候可以不加版本信息;
  2. starter:在starter中定义了一些基础的jar包依赖,所以我们在使用框架的时候只需要引入starter即可,不必自己去引入一些相关的依赖。

2、自动装配

我们在使用springboot的时候都会在启动类上添加org.springframework.boot.autoconfigure.SpringBootApplication而他又是一个组合注解

org.springframework.boot.SpringBootConfiguration注解:表示当前类是一个配置类

org.springframework.boot.autoconfigure.EnableAutoConfiguration:开启自动装配

  1. org.springframework.boot.autoconfigure.AutoConfigurationPackage:获取到启动类所在的包信息
  2. org.springframework.context.annotation.Import:引入org.springframework.boot.autoconfigure.AutoConfigurationImportSelector过滤需要自动装配的配置类信息

org.springframework.context.annotation.ComponentScan:扫描当前包以及子包下的组件到容器中

我们再来总结一下springboot的启动流程里都做了啥

1、构造org.springframework.boot.SpringApplication对象发

  • 设置主类信息
  • 设置应用类型
  • 设置初始化器
  • 设置监听器

2、执行run方法

  • 获取并启动监听器
  • 准备上下文环境
  • 创建上下文
  • 上下文前置处理
  • 刷新上下文
  • 上下文后置处理
  • 运行runner