003.SpringBoot启动原理

本节目标

  • springboot为什么可以省略很多常用jar包的版本呢?
  • 为什么能扫描启动类及其下面的注解类加入到spring容器中呢?
  • 为什么可以简化springmvc以前那么多xml配置文件呢?

本节就是解决上面的疑惑,从整理上有个了解。虽然这些内容的了解对实际开发没有太大帮助,但是确能深入理解一下springboot的自动化配置的原理。对于以后更深入的开发--比如做自己的启动器有很大帮助。

依赖导入原理

依赖spring-boot-dependencies两种方式,在前两节介绍过了,这里汇总下:

  • 继承父工程方式

    xml 复制代码
    <!--继承父项目方式-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.10</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
  • 导入springboot 依赖方式

    xml 复制代码
    <dependencyManagement>
        <dependencies>
            <!--==================================== -->
            <!-- springboot 依赖 -->
            <!--==================================== -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.7.10</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

不管哪种方式,他们都是殊途同归的引入spring-boot-dependencies-版本.pom文件。也正是由于依赖这个pom文件,很多常用jar包的版本都定义好了,如下部分内容:

xml 复制代码
<properties>
    <activemq.version>5.16.6</activemq.version>
    <antlr2.version>2.7.7</antlr2.version>
    <appengine-sdk.version>1.9.98</appengine-sdk.version>
    <artemis.version>2.19.1</artemis.version>
    <aspectj.version>1.9.7</aspectj.version>
    <assertj.version>3.22.0</assertj.version>
    <atomikos.version>4.0.6</atomikos.version>
    <awaitility.version>4.2.0</awaitility.version>
    <build-helper-maven-plugin.version>3.3.0</build-helper-maven-plugin.version>
    <byte-buddy.version>1.12.23</byte-buddy.version>
    <cache2k.version>2.6.1.Final</cache2k.version>
    <caffeine.version>2.9.3</caffeine.version>
    <cassandra-driver.version>4.14.1</cassandra-driver.version>
    <classmate.version>1.5.1</classmate.version>
    <commons-codec.version>1.15</commons-codec.version>
    ......

这里管理着springboot中所有依赖的版本,导入依赖如果不写版本就用这里的版本。

两个重要的注解

typescript 复制代码
@SpringBootApplication
public class Springboot02Application {
    public static void main(String[] args) {
        //SpringApplication.run(Springboot02Application.class, args);
        SpringApplication app = new SpringApplication(Springboot02Application.class);
        app.setBannerMode(Banner.Mode.OFF);
        app.run(args);
    }
}

在我们的springboot启动类上加入该注解。这个注解说明该类为SpringBoot的主配置类。SpringBoot就应该运行该类的main方法来启动SpringBoot项目。进入这个注解,可以发现它是一个组合注解。

less 复制代码
@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 {

其中包含两个非常重要的注解:

  • @SpringBootConfiguration
  • @EnableAutoConfiguration

@SpringBootConfiguration

css 复制代码
@SpringBootConfiguration

在我们的springboot启动类上加入该注解。这个注解说明该类为SpringBoot的主配置类。SpringBoot就应该运行该类的main方法来启动SpringBoot项目。进入这个注解,可以发现它是一个组合注解,其中包含两个非常重要的

进入@SpringBootApplication注解,发现还有一个注解@Conguration,说明它本身就是个配置类。

less 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {

进入@Conguration注解,发现它还有个注解@Component,说明它也是容器中的一个组件。

less 复制代码
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {

@EnableAutoConfiguration

该注解为SpringBoot实现自动配置的核心注解。按照英文的字面意思:开启自动配置,也能联想到,有了这个注解我们之前需要配置的一大堆的xml配置文件,现在不需要配置了。进入该注解:

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

其中也有两个非常重要的注解:

  • @AutoConfigurationPackage
  • @Import(AutoConfigurationImportSelector.class)

下来详细看看这两个注解做的事情。

1.@AutoConfigurationPackage 自动配置包

css 复制代码
@AutoConfigurationPackage

我们进入该注解,发现这个注解有一个@Import注解

less 复制代码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

@Import(AutoConfigurationPackages.Registrar.class)这个@import有点类似我们以前在springMVC配置文件中的配置文件导入,回忆一下spring.xml:

xml 复制代码
<!-- 导入redis的相关配置 -->
<import resource="classpath:spring/spring-redis.xml" />

它的作用和上面回忆中的意思差不多,就是给容器中导入一个组件,也就是导入 AutoConfigurationPackages.Registrar.class 的组件 ,那我们也进入 Registrar 类的内部看看

通过registerBeanDefinitions【注册很多Bean的定义】这个命名不难猜出,这里要把很多的bean注册到spring容器中,我们在register方法前加一个断点,看看这个注册类的父包是什么。这里可以debug进行调试观察,选中new PackageImports(metadata).getPackageNames()右键选Evaluate Expression,如下图:

然后在弹出对话框中点击Evaluate按钮:

总结: @AutoConfigurationPackag 的作用就是将SpringBoot 主配置类所在的包及其下面的所有子包里面的所有组件扫描到 Spring 容器中。这也就是为什么要求我们创建的所有类和包,最好在启动类的同目录或者子类目下了。

2.@Import(AutoConfigurationImportSelector.class)

python 复制代码
@Import(AutoConfigurationImportSelector.class)

该注解的作用就是给容器中导入组件,这个注解需要导入的组件就是 AutoConfigurationImportSelector,也就是自动配置导入选择器,它可以帮我们选择需要导入的组件。进入该类,可以看到这么一个方法:

加上断点对 springboot02 进行debug启动,可以看到有144个配置类:

我们仅仅在项目中引入了一个web依赖:

xml 复制代码
<!-- web项目相关依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

但是却又144个自动配置类自动加载了。这也就是为什么我们不需要进行繁琐的xml配置的根本原因了。自动配置类给我们当前项目的场景提供了一些组件和配置,有了自动配置就避免了手动编写配置文件,注入等等功能

在我们断点的下面有一句断言的代码:

arduino 复制代码
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.");

大概意思是在META-INF/spring.factories或META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports中找不到配置类。这说明加载自动配置类就是从这两个地方找的。我们打开这autoConfiguration.imports看看:

可以看到里面有很多的配置类。意味着很多很多配置文件就设置好了。对应的默认值,也都设置好了。就好比默认加载如下配置文件

记忆印记

  • 继承父工程或导入spring-boot-dependencies使我们不用关注常用依赖的版本号
  • @SpringBootConfiguration:表明我们的springboot启动类也是一个配置类同时被spring容器管理
  • @AutoConfigurationPackag:能扫描启动类及其下面的所有的子包
  • AutoConfigurationImportSelector.class:加载了spring-boot-autoconfigure下所有的自动配置类
相关推荐
开心工作室_kaic40 分钟前
ssm010基于ssm的新能源汽车在线租赁管理系统(论文+源码)_kaic
java·前端·spring boot·后端·汽车
代码吐槽菌41 分钟前
基于SSM的汽车客运站管理系统【附源码】
java·开发语言·数据库·spring boot·后端·汽车
zdkdchao1 小时前
jdk,openjdk,oraclejdk
java·开发语言
精致先生2 小时前
问题记录01
java·数据库·mybatis
小魏冬琅2 小时前
探索面向对象的高级特性与设计模式(2/5)
java·开发语言
TT哇2 小时前
【Java】数组的定义与使用
java·开发语言·笔记
look_outs3 小时前
JavaSE笔记2】面向对象
java·开发语言
武子康3 小时前
大数据-191 Elasticsearch - ES 集群模式 配置启动 规划调优
java·大数据·elk·elasticsearch·搜索引擎·全文检索
A_aspectJ3 小时前
‌Spring MVC的主要组件有哪些?
java·spring·mvc
塔塔开!.3 小时前
Maven的依赖
java·maven