百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

Spring Boot 运行原理(5分钟速解)

wptr33 2025-05-02 21:38 18 浏览

Spring Boot项目到底是怎么运行的呢?Maven项目,我们一般从pom.xml文件探究起。

如果想年后找到更好的工作,推荐看这篇文章

Java后端面试复习规划表,5万字

1、pom.xml探究

1.1、父依赖

(1)Spring Boot项目主要依赖的是一个父项目(
spring-boot-starter-parent
)。

pom.xml文件:

<!-- 父项目 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.7</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

注:
spring-boot-starter-parent
项目的主要作用是:

  • 对项目的资源进行过滤。
  • spring-boot-starter-parent-2.7.7.pom文件:
  <!-- 资源过滤 -->
  <resources>
      <resource>
          <directory>${basedir}/src/main/resources</directory>
          <filtering>true</filtering>
          <includes>
              <include>**/application*.yml</include>
              <include>**/application*.yaml</include>
              <include>**/application*.properties</include>
          </includes>
      </resource>
      <resource>
          <directory>${basedir}/src/main/resources</directory>
          <excludes>
              <exclude>**/application*.yml</exclude>
              <exclude>**/application*.yaml</exclude>
              <exclude>**/application*.properties</exclude>
          </excludes>
      </resource>
  </resources>
  • 对项目的插件进行管理。
  • spring-boot-starter-parent-2.7.7.pom文件:
  <!-- 插件管理(部分) -->
  <pluginManagement>
        <plugins>
          <plugin>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-maven-plugin</artifactId>
            <version>${kotlin.version}</version>
            <configuration>
              <jvmTarget>${java.version}</jvmTarget>
              <javaParameters>true</javaParameters>
            </configuration>
            <executions>
              <execution>
                <id>compile</id>
                <phase>compile</phase>
                <goals>
                  <goal>compile</goal>
                </goals>
              </execution>
              <execution>
                <id>test-compile</id>
                <phase>test-compile</phase>
                <goals>
                  <goal>test-compile</goal>
                </goals>
              </execution>
            </executions>
          </plugin>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
              <parameters>true</parameters>
            </configuration>
          </plugin>
          ……        
        </plugins>
      </pluginManagement>

(2)点击进入该父项目(
spring-boot-starter-parent
),发现它还有一个父项目(spring-boot-dependencies)。


spring-boot-starter-parent-2.7.7.pom
文件:

<!-- 父项目的父项目 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.7.7</version>
</parent>

spring-boot-dependencies项目才是真正管理SpringBoot应用中所有依赖及其版本的地方。spring-boot-dependencies项目的主要作用是:

  • SpringBoot项目中依赖的所有jar包的版本进行管理。
  • spring-boot-dependencies-2.7.7.pom文件:
  <!-- 依赖包版本管理(部分) -->
  <properties>
      <activemq.version>5.16.5</activemq.version>
      <antlr2.version>2.7.7</antlr2.version>
      <appengine-sdk.version>1.9.98</appengine-sdk.version>    
      <build-helper-maven-plugin.version>3.3.0</build-helper-maven-plugin.version>    
      <commons-pool.version>1.6</commons-pool.version>
      <commons-pool2.version>2.11.1</commons-pool2.version>
      <db2-jdbc.version>11.5.7.0</db2-jdbc.version>
      <dependency-management-plugin.version>1.0.15.RELEASE</dependency-management-plugin.version>    
      <git-commit-id-plugin.version>4.9.10</git-commit-id-plugin.version>    
      <h2.version>2.1.214</h2.version>    
      <hibernate.version>5.6.14.Final</hibernate.version>
      <hibernate-validator.version>6.2.5.Final</hibernate-validator.version>    
      <htmlunit.version>2.60.0</htmlunit.version>
      <httpclient.version>4.5.14</httpclient.version>
      <httpclient5.version>5.1.4</httpclient5.version>
      <httpcore.version>4.4.16</httpcore.version>
      <httpcore5.version>5.1.5</httpcore5.version>    
      <javax-activation.version>1.2.0</javax-activation.version>
      <javax-annotation.version>1.3.2</javax-annotation.version>
      <javax-cache.version>1.1.1</javax-cache.version>    
      <javax-json.version>1.1.4</javax-json.version>
      <javax-jsonb.version>1.0</javax-jsonb.version>
      <javax-mail.version>1.6.2</javax-mail.version>
      <javax-money.version>1.1</javax-money.version>    
      <javax-websocket.version>1.1</javax-websocket.version>
      <jaxen.version>1.2.0</jaxen.version>    
      <json-path.version>2.7.0</json-path.version>
      <json-smart.version>2.4.8</json-smart.version>
      <jsonassert.version>1.5.1</jsonassert.version>
      <jstl.version>1.2</jstl.version>
      <jtds.version>1.3.1</jtds.version>
      <junit.version>4.13.2</junit.version>
      <junit-jupiter.version>5.8.2</junit-jupiter.version>
      <kafka.version>3.1.2</kafka.version>    
      <log4j2.version>2.17.2</log4j2.version>
      <logback.version>1.2.11</logback.version>
      <lombok.version>1.18.24</lombok.version>
      <maven-clean-plugin.version>3.2.0</maven-clean-plugin.version>
      <maven-compiler-plugin.version>3.10.1</maven-compiler-plugin.version>
      <maven-dependency-plugin.version>3.3.0</maven-dependency-plugin.version>
      <maven-deploy-plugin.version>2.8.2</maven-deploy-plugin.version>
      <maven-help-plugin.version>3.2.0</maven-help-plugin.version>
      <maven-install-plugin.version>2.5.2</maven-install-plugin.version>
      <maven-invoker-plugin.version>3.2.2</maven-invoker-plugin.version>
      <maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
      <maven-javadoc-plugin.version>3.4.1</maven-javadoc-plugin.version>
      <maven-resources-plugin.version>3.2.0</maven-resources-plugin.version>
      <maven-war-plugin.version>3.3.2</maven-war-plugin.version>    
      <mongodb.version>4.6.1</mongodb.version>    
      <mysql.version>8.0.31</mysql.version>    
      <rabbit-amqp-client.version>5.14.2</rabbit-amqp-client.version>
      <rabbit-stream-client.version>0.5.0</rabbit-stream-client.version>
      <spring-framework.version>5.3.24</spring-framework.version>
      <spring-kafka.version>2.8.11</spring-kafka.version>
      <spring-security.version>5.7.6</spring-security.version>
      <spring-session-bom.version>2021.2.0</spring-session-bom.version>
      <sqlite-jdbc.version>3.36.0.3</sqlite-jdbc.version>
      <thymeleaf.version>3.0.15.RELEASE</thymeleaf.version>
      <thymeleaf-extras-data-attribute.version>2.0.1</thymeleaf-extras-data-attribute.version>
      <thymeleaf-extras-java8time.version>3.0.4.RELEASE</thymeleaf-extras-java8time.version>
      <thymeleaf-extras-springsecurity.version>3.0.5.RELEASE</thymeleaf-extras-springsecurity.version>
      <thymeleaf-layout-dialect.version>3.0.0</thymeleaf-layout-dialect.version>
      <tomcat.version>9.0.70</tomcat.version>    
      <versions-maven-plugin.version>2.10.0</versions-maven-plugin.version>
      <webjars-locator-core.version>0.50</webjars-locator-core.version>    
      <xml-maven-plugin.version>1.0.2</xml-maven-plugin.version>
      <xmlunit2.version>2.9.0</xmlunit2.version>
  </properties>

因此,以后我们导入依赖包时默认是不需要配置版本的;但是如果导入的包没有在依赖中管理,那么就需要我们手动配置版本了。

注:Spring Boot官方文档列出Spring Boot项目中我们可以使用的所有依赖及其默认版本。

1.2、启动器

所谓“启动器”,即:Spring Boot应用依赖的一个个spring-boot-starter-XXX项目。

SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),我们只需要在项目中引入这些starter即可将所有相关的依赖都导入进来 , 我们要用什么功能就导入什么样的场景启动器即可 ;我们未来也可以自己自定义starter

如:spring-boot-starter-web启动器:帮我们导入了web模块正常运行所依赖的组件。

pom.xml文件:

<!-- web场景启动器 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

注:Spring Boot官方文档列出Spring Boot项目中我们可以使用的所有启动器。

2、主启动类运行原理

2.1、默认的主启动类


Springboot01HelloworldApplication.java
文件:

package com.atangbiji;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

//@SpringBootApplication用来标注一个主程序类,说明这是一个Spring Boot应用
@SpringBootApplication
public class Springboot01HelloworldApplication {
    //SpringBoot应用程序的主入口函数
    public static void main(String[] args) {
        //以为是启动了一个方法,没想到启动了一个服务
        SpringApplication.run(Springboot01HelloworldApplication.class, args);
    }
}

但是一个简单的启动类并不简单!主启动类主要通过@SpringBootApplication注解和SpringApplication类调用run方法启动,接下来我们来分析一下它们都干了什么。

注:Spring Boot项目启动后,会像Java应用启动一样,会在后台启动一个几百兆的Java进程。如下图所示:

该进程对应的进程号PID)通常我们可以在Spring Boot的启动log中查看。如下图所示:

2.2、@SpringBootApplication注解

作用:@SpringBootApplication用来标注一个主程序类,说明这是一个Spring Boot应用。 SpringBoot就通过运行这个类的main方法来启动SpringBoot应用。

点击进入SpringBootApplication注解:可以看到上面还有很多其他注解!


SpringBootApplication.java
文件:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//SpringBoot的配置类
@SpringBootConfiguration
//开启自动配置功能
@EnableAutoConfiguration
//扫描XX包下的组件或者bean
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
   // ......
}

2.2.1、@ComponentScan注解

@ComponentScan注解在Spring中很重要,它对应XML配置中的元素。

作用:自动扫描并加载主启动类(
Springboot01HelloworldApplication
同级目录下符合条件的组件或者bean,将这个bean定义加载到IOC容器中。

2.2.2、@SpringBootConfiguration注解

作用:SpringBoot的配置类,标注在某个类上,表示这是一个SpringBoot的配置类。

2.2.2.1、@Configuration注解

点击进入@SpringBootConfiguration注解:可以看到上面还有很多其他注解!


SpringBootConfiguration.java
文件:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
//配置类
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    // ......
}

这里的@Configuration注解说明:这是一个配置类。配置类就对应Springxml配置文件。

2.2.2.2、@Component注解

点击进入@Configuration注解:可以看到它也是一个组件(Component)!

Configuration.java文件:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
//组件(Bean)
@Component
public @interface Configuration {
    // ......
}

这里的@Component注解说明:主启动类本身也是Spring中的一个组件(Bean)而已,负责启动Spring Boot应用!

接下来,我们回到SpringBootApplication注解中继续分析。

2.2.3、@EnableAutoConfiguration注解(重点)

作用:开启自动配置功能。

以前我们需要自己手动配置的东西,而现在SpringBoot可以自动帮我们配置 ;通过@EnableAutoConfiguration注解让SpringBoot开启自动配置功能后,自动配置才能生效。

2.2.3.1、@AutoConfigurationPackage注解

(1)点击进入@EnableAutoConfiguration注解:可以看到它包含一个@AutoConfigurationPackage注解。


EnableAutoConfiguration.java
文件:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//自动配置包
@AutoConfigurationPackage
//(向IOC容器中)导入自动配置导入选择器
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    // ......
}

这里的@AutoConfigurationPackage注解的作用是:自动配置包。

(2)点击进入@AutoConfigurationPackage注解:可以看到它通过@Import(
AutoConfigurationPackages.Registrar.class)
注解向容器中导入“自动配置包注册器”组件。


AutoConfigurationPackage.java
文件:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
//向容器中导入“自动配置包注册器”组件(Bean)
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
    // ......
}

注:

  • 这里的@import注解是Spring的底层注解,它的作用是:向容器中导入组件(Bean)。
  • 这里的Registrar.class的作用是:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring IOC容器中。

接下来,返回上一级,我们回到EnableAutoConfiguration注解中继续分析。

2.2.3.2、通过@Import注解导入自动配置导入选择器

点击进入@EnableAutoConfiguration注解:可以看到它通过@Import({
AutoConfigurationImportSelector.class})
注解,向IOC容器中导入自动配置导入选择器
AutoConfigurationImportSelector
)组件(Bean)。

自动配置导入选择器会导入哪些组件的选择器呢?

(1)点击进入
AutoConfigurationImportSelector
类,可以发现:这个类中有一个
getCandidateConfigurations
方法。


AutoConfigurationImportSelector.java
文件:

//获取候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = new ArrayList<>(
        SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
    //导入候选的配置
    ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
    Assert.notEmpty(configurations,
                    "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
                    + "are using a custom packaging, make sure that file is correct.");
    return configurations;
}

注:

  • getCandidateConfigurations方法的作用是:获取候选的配置。
  • 该方法通过调用ImportCandidatesload方法导入候选的配置!

(2)点击进入ImportCandidates类的load()方法,可以发现:其中加载了本地META-INF/spring目录下的自动配置核心文件(即
org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件)。

ImportCandidates.java文件:

//自动配置核心文件的本地目录
private static final String LOCATION = "META-INF/spring/%s.imports";
//导入候选的配置
public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
    Assert.notNull(annotation, "'annotation' must not be null");
    ClassLoader classLoaderToUse = decideClassloader(classLoader);
    //加载本地的自动配置核心文件
    String location = String.format(LOCATION, annotation.getName());
    Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
    List<String> importCandidates = new ArrayList<>();
    while (urls.hasMoreElements()) {
        URL url = urls.nextElement();
        importCandidates.addAll(readCandidateConfigurations(url));
    }
    return new ImportCandidates(importCandidates);
}

(3)查看Spring Boot自动配置的核心文件。


org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件是Spring Boot自动配置的核心文件。它位于
spring-boot-autoconfigure-2.7.7.jar
包的META-INF/spring目录下,如下图所示:

注:

  • Spring Boot自动配置的核心文件中的所有类(Beans)都是通过java配置类的方式显式地配置到Spring IOC容器中的。
  • Spring Boot自动配置的核心文件中没有的类(Beans)都需要我们手动进行配置。

(4)我们在自动配置的核心文件中随便找一个自动配置类进行全局搜索,如:WebMvcAutoConfiguration

可以发现:这些类的都是JavaConfig配置类,而且都注入了一些Bean。我们可以找一些自己认识的自动配置类,看着熟悉一下!

所以,自动配置真正实现是从classpath中搜寻所有META-INF/spring/目录下的
org.springframework.boot.autoconfigure.AutoConfiguration.imports
配置文件,并将其中对应的配置项,通过反射实例化为自动配置类,然后将这些都汇总成为一个实例并加载到Spring IOC容器中。

2.2.3.3、结论

(1)SpringBoot所有的自动配置都是在主启动类启动时扫描并加载的。

(2)SpringBoot自动配置好的这些组件(Bean),需要我们导入对应的启动器(spring-boot-starter-XXX)后才会生效。即:只有@ConditionalOnXXX注解中的条件都满足时,自动配置的相应组件(Bean)才会生效。

(3)SpringBoot在启动的时候,从类路径(classpath)的META-INF/spring目录下的
org.springframework.boot.autoconfigure.AutoConfiguration.imports
配置文件中获取值。

(4)SpringBoot将这些值作为自动配置类导入Spring IOC容器,自动配置类就生效,帮我们进行自动配置工作。

(5)整个J2EE的整体解决方案和自动配置都在spring-boot-autoconfigurejar包中。

(6)SpringBoot会向容器中导入非常多的自动配置类(xxxAutoConfiguration),就是向容器中导入这个场景需要的所有组件,并配置好这些组件。

(7)有了自动配置类,免去了我们手动编写配置注入功能组件等工作。

2.3、SpringApplication类

2.3.1、SpringApplication类的作用

SpringApplication类主要完成以下四件事情:

  • 推断应用的类型是普通的项目还是Web项目。
  • 查找并加载所有可用初始化器,并设置到initializers(初始化)属性中。
  • 找出所有的应用程序监听器,设置到listeners属性中。
  • 推断并设main方法的定义类,找到运行的主类。

好了,今天就分享到这里,如果有点点帮助,记得点赞、收藏、分享,谢啦!

相关推荐

MySql系列-常用命令

本篇是对...

Record.ToTable 格式转换

本期案例对表格格式进行转换,前后转换效果如下:解题套路1.Record.ToTable解题思路:思路就是构造可以透视的样式。使用Record.ToTable对行记录进行转换,获得包含两列的表,首行可以...

Table.Group 按时期累计计算唯一值

本期案例是根据不同id进行汇总统计:组内,相同日期的为一组,统计“from”、“to”中的非重复个数;连续日期的,统计累计数。前后转换效果如下:解题套路1.Table.Group...

MySQL 9.1正式发布,有哪些值得关注的新特性?

MySQL创新版9.1.0于2024年10月15日正式发布。此外,MySQL8.0.40及8.4.3补丁版本也同时发布。8.4.3是目前MySQL的LTS长期支持版本,该版本中将不会增加新的功能与特性...

SQL基本语句练习(基础版)

最近在学习SQL基本语句的练习,在此分享一下笔者做过的练习以及个人的解决教程:首先是基本练习表格的搭建,具体内容如下表所示:...

SQL 从入门到精通:全面掌握数据库操作

学习SQL(StructuredQueryLanguage)是掌握数据库操作的关键步骤。SQL是一种用于管理和处理关系型数据库的标准语言,广泛应用于数据检索、插入、更新和删除等操作。以下是一些...

ClickHouse学习笔记四ClickHouse基础语法

前言这里我们介绍ClickHouse的基本语法,使用环境是腾讯云的ClickHouse。默认情况下,ClickHouse在进行集群纬度执行建表等DDL操作时需要手动添加ONCLUSTERX...

程序员总结的常用sql语句大全

多年经验程序员总结的我们一般需要使用的sql语句,赶快收藏起来,方便以后使用。以下是一些常用的SQL语句及其用法:一、数据定义语言(DDL)创建库CREATEDATABASE:创建一个新数据库。...

PQ03-分组求和

目标已知:销售清单求:每个销售员的销量合计方法数据准备...

好荐:一款数据库元数据管理平台工具

“元数据”的定义在不同的软件、项目、工程的定义范围都不太一样。本文这里指的是软件项目开发使用的数据库表结构信息。我今天介绍的这个开源项目叫Databasir,它是一个面向团队的关系型数据库模型文档管理...

MySQL 8.0 SQL优化黑科技,面试官都不一定知道!

前言提到SQL优化,大多数人想到的还是那些经典套路:建索引、避免全表扫描、优化JOIN顺序…这些确实是基础,但如果你还停留在MySQL5.7时代的优化思维,那就out了。MySQL8.0已经发布好...

MySQL数据库深度优化指南:从基础到架构层面的20个关键策略

一、核心性能优化原则数据最小化原则...

动物源性食品中兽药残留的检测——喹啉类药物残留

喹啉类药物(quinoxaline)是具有喹啉-N1,N4-二氧化物基本结构的一类化学合成的动物专用药,具有广谱抗菌、提高饲料转化率和促生长作用。1965年德国拜耳公司以邻硝基苯胺为原料合成喹乙醇(o...

适合普通开发者和产品经理的PHP应用模板开发AI的SaaS应用框架

简单到傻!Liang_SaaS适合普通开发者和产品经理的PHP应用模板开发AI的SaaS应用框架,利用Php开发AI的SaaS应用框架,是一个强大的内容管理仪表板模板,基于Bootstrap和...

Power Query 交错合并表格的方法

两张表格合并成一张表格,需要交错排列,表1取一行,表2取一行,这样排列在一起:前提是两张表的行数相同,内容排列顺序相同:我们来看两张表:表1:12列10行表2:11列10行行数相同列数不同,我们在数据...