1、Spring Boot简介

1.1、回顾什么是Spring

Spring是一个开源框架,2003年兴起的一个轻量级的Java开发框架,作者:Rod Johnson(音乐学博士)。

Spring是为了解决企业级应用开发的复杂性而创建的,简化开发。

1.2、Spring是如何简化Java开发的

为了降低Java开发的复杂性,Spring采用了以下4种关键策略:

1、基于POJO的轻量级和最小侵入性编程,所有东西都是bean

2、通过IOC,依赖注入(DI)和面向接口实现松耦合;

3、基于切面(AOP)和惯例进行声明式编程;

4、通过切面和模版减少样式代码,RedisTemplatexxxTemplate

1.3、什么是Spring Boot

学过javaweb的同学就知道:开发一个web应用,从最初开始接触Servlet结合Tomcat,跑出一个Hello Wolrld程序,是要经历特别多的步骤;后来就用了框架Struts,再后来是SpringMVC,到了现在的Spring Boot,过一两年又会有其他web框架出现;你们有经历过框架不断的演进,然后自己开发项目所有的技术也在不断的变化、改造吗?建议都可以去经历一遍。

言归正传,什么是Spring Boot呢?就是一个javaweb的开发框架,和SpringMVC类似,对比其他javaweb框架的好处,官方说是简化开发,约定大于配置you can "just run",能迅速的开发web应用,几行代码开发一个http接口。

所有的技术框架的发展似乎都遵循了一条主线规律:从一个复杂应用场景衍生一种规范框架,人们只需要进行各种配置而不需要自己去实现它,这时候强大的配置功能成了优点;发展到一定程度之后,人们根据实际生产应用情况,选取其中实用功能和设计精华,重构出一些轻量级的框架;之后为了提高开发效率,嫌弃原先的各类配置过于麻烦,于是开始提倡“约定大于配置”,进而衍生出一些一站式的解决方案。

是的,这就是Java企业级应用→J2EEspringSpring Boot的过程。

随着Spring不断的发展,涉及的领域越来越多,项目整合开发需要配合各种各样的文件,慢慢变得不那么易用简单,违背了最初的理念,甚至人称配置地狱。Spring Boot正是在这样的一个背景下被抽象出来的开发框架,目的为了让大家更容易的使用Spring、更容易的集成各种常用的中间件、开源软件。

Spring Boot基于Spring开发,SpirngBoot本身并不提供Spring框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于Spring框架的应用程序。也就是说,它并不是用来替代Spring的解决方案,而是和Spring框架紧密结合用于提升Spring开发者体验的工具。Spring Boot约定大于配置的核心思想,默认帮我们进行了很多设置,多数Spring Boot应用只需要很少的Spring配置。同时它集成了大量常用的第三方库配置(例如RedisMongoDBJpaRabbitMQQuartz等等),Spring Boot应用中这些第三方库几乎可以零配置的开箱即用

简单来说就是Spring Boot其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,Spring Boot整合了所有的框架 。

Spring Boot出生名门,从一开始就站在一个比较高的起点,又经过这几年的发展,生态足够完善,Spring Boot已经当之无愧成为 Java领域最热门的技术。

1.4、Spring Boot的主要优点

  • 为所有Spring开发者更快的入门;
  • 开箱即用,提供各种默认配置来简化项目配置;
  • 内嵌式容器简化Web项目;
  • 没有冗余代码生成和XML配置的要求。

2、微服务架构

2.1、什么是微服务

微服务是一种架构风格。它要求我们在开发一个应用的时候,这个应用必须构建成一系列小服务的组合;可以通过http等方式进行互通。

2.2、单体应用架构

要说微服务架构,先得说说过去的单体应用架构。

所谓“单体应用架构(all in one)”是指:我们将一个应用中的所有应用服务都封装在一个应用中。无论是ERPCRM或其它系统,我们都把数据库访问、Web访问等功能放在一个war包内。

all in one的架构方式,我们把所有的功能单元放在一个应用里面,然后把整个应用部署到服务器上。如果负载能力不行,我们将整个应用进行水平复制,进行扩展,然后再负载均衡。

  • 这样做的好处是:易于开发和测试;方便部署;当需要扩展时,只需将war包复制多份,然后放到多个服务器上,再做个负载均衡就可以了。
  • 单体应用架构的缺点是:哪怕要修改一个非常小的地方,我们都要停掉整个服务,重新打包、部署整个应用的war包。特别是对于一个大型应用,我们不可能把所有内容放在一个应用里面,我们如何维护、如何分工合作都是问题。

2.3、微服务架构

SpringCloud架构

所谓“微服务架构”,就是打破之前“all in one”的架构方式,把每一个功能元素独立出来。把独立出来的功能元素动态组合,需要的功能元素才拿来组合,需要多一些时可以整合多个功能元素。所以,微服务架构是对功能元素进行复制,而没有对整个应用进行复制。

这样做的好处是:

  • 节省了调度资源。
  • 每个功能元素的服务都是一个可替换的、可独立升级的软件代码。
单体应用与微服务

注:Martin Fowler于2014年3月25日写的《Microservice》,详细阐述了什么是微服务。

2.4、如何构建微服务

SpringCloud的微服务弹性

一个大型系统的微服务架构,就像一个复杂交织的神经网络,每一个神经元就是一个功能元素,它们各自完成自己的功能,然后通过http互相请求调用。比如一个电商系统,查缓存、连接数据库、浏览页面、结账、支付等服务都是一个个独立的功能服务,它们都被微服务化了,它们作为一个个微服务共同构建了一个庞大的系统。如果修改其中的一个功能,只需更新升级其中的一个功能服务单元即可。

但是这种庞大的系统架构给部署和运维带来了很大的难度。于是,Spring为我们带来了构建大型分布式微服务的全套产品,主要包括:

  • 构建一个个功能独立的微服务应用单元,可以使用Spring Boot,它可以帮助我们快速构建一个应用(微服务)。
  • 大型分布式网络服务的调用,这部分由Spring Cloud来完成,实现分布式。
  • 在分布式中间,进行流式数据计算、批处理,我们有Spring cloud data flow
  • Spring为我们想清楚了整个从开始构建应用到大型分布式应用的全流程方案。

SpringBoot与SpringCloud

3、第一个Spring Boot应用程序

我们将学习如何快速的创建一个Spring Boot应用,并且实现一个简单的Http请求处理。通过这个例子对Spring Boot有一个初步的了解,并体验其结构简单、开发快速的特性。

3.1、准备工作

(1)环境准备:

  • java version "1.8.0_181"

  • Maven-3.6.1

  • Spring Boot 2.7.7

    注:Spring Boot 3.0以上版本最低支持 Java 17

  • MySQL 5.7.19

(2)开发工具:

  • IDEA 2020.2

3.2、创建Spring Boot项目的两种方式

附:创建基础项目说明

  • Spring官方为我们提供了一个快速构建(初始化)应用的网站Spring Initializrhttps://start.spring.io/
  • IDEA集成了这个网站!

3.2.1、方式1:使用Spring Initializr创建项目

使用SpringInitializr创建

主要步骤如下:

(1)打开 https://start.spring.io/

(2)填写项目信息。

(3)点击”Generate Project“按钮生成项目、下载项目。

(4)解压项目包,并用IDEAMaven项目导入,一路下一步即可,直到项目导入完毕。

**注:**如果是第一次使用,可能速度会比较慢,包比较多、需要耐心等待一切就绪。

3.2.2、方式2:使用IDEA创建项目(推荐)

主要步骤如下:

(1)点击FileNewProject菜单,创建一个新的Spring Initializr项目。

IDEA新建项目

注:IDEA默认去Spring官网的快速构建工具那里下载项目文件。

(2)填写项目信息。项目名称为springboot-01-helloworld

IDEA填写项目信息

(3)选择初始化的组件(初学勾选Spring Web即可)和Spring Boot的版本(我们这里选择2.7.7版)。

IDEA选择初始化的组件

注:Spring Boot 3.0以上版本最低支持 Java 17

(4)填写项目路径。

IDEA填写项目路径

(5)等待项目构建成功即可。

附1:配置本地Maven

Spring Boot项目创建过程中,可能会出现IDEA的本地Maven仓库配置不正确的问题。错误如下图所示:

IDEA配置Maven仓库出错

此时,我们需要将Maven设置成我们自己的Maven,具体实现步骤如下:

  • 先删除IDEA自动生成的.mvn文件夹、.gitignoreHELP.mdmvnwmvnw.cmd文件。如下图所示:

    删除多余文件

    注:

    • 我们必须将.mvn/wrapper/maven-wrapper.jar删除,否则我们无法我们自己的Maven安装目录。

    • Maven未识别到该子项目,则右键为其添加Maven支持。

      添加Maven支持
  • 然后,在Settings中手动配置Maven的安装目录、配置文件和本地仓库。如下图所示:

IDEA手动配置Maven
  • 重新编译项目,并重新加载Maven项目。如下图所示:

重新编译加载项目

附2:解决单元测试包不存在的问题

由于Spring Boot2.2之前版本使用的是Junit4,而Spring Boot2.2之后的版本的使用的是Junit5。若出现spring boot org.junit.jupiter.api包不存在的问题,则需要在pom.xml文件中手动导入Junit5相关的依赖包。

pom.xml文件:

1
2
3
4
5
6
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.5.0</version>
<scope>test</scope>
</dependency>

3.3、项目初始结构分析

通过上面步骤完成了基础项目的创建。就会自动生成以下文件:

(1)程序的主启动类(入口)及main函数。

Springboot01HelloworldApplication.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.atangbiji;

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

//SpringBoot应用程序的主入口
@SpringBootApplication
public class Springboot01HelloworldApplication {

public static void main(String[] args) {
SpringApplication.run(Springboot01HelloworldApplication.class, args);
}
}

(2)一个application.properties配置文件。

(3)一个测试类。

(4)一个pom.xml文件。

pom.xml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<!-- 父依赖 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!-- Maven项目的GAV(坐标) -->
<groupId>com.atangbiji</groupId>
<artifactId>springboot-01-helloworld</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-01-helloworld</name>
<description>Demo project for Spring Boot</description>
<properties>
<!-- Java版本 -->
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- web场景启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- springboot单元测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.5.0</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<!-- 打包插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

**注:**所有的Spring Boot的依赖的项目名称都是spring-boot-starter-XXX,它们又被称作“启动器”。

3.4、建立项目的基本结构

在程序的主启动类(Springboot01HelloworldApplication)的同级目录下:

  • 新建一个controller包,用于存放控制器代码;
  • 新建一个service包,用于存放业务层代码;
  • 新建一个dao包,用于存放持久层代码(映射器接口及其对应的映射器配置文件);
  • 新建一个pojo包,用于存放实体类;
  • 新建一个utils包,用于存放我们封装的各种工具。

**注:**程序的主启动类一定要与controllerservicedaopojoutils包在同级目录下,否则识别不到【约定大于配置】。

3.5、编写控制器接口

controller包下新建一个HelloController类。

HelloController.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.atangbiji.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

//控制器
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello() {
return "Hello World,Spring Boot!";
}
}

3.6、启动测试

  • 启动主启动类中的main函数,如下图所示:
启动测试

此时,Tomcat启动成功。如下图所示:

启动成功
  • 在浏览器中输入http://localhost:8080/hello,页面访问成功。访问结果如下图所示:
SpringBoot项目启动成功

3.7、将项目打成jar包

双击mavenpackage(打包)按钮,对项目进行打包。若打包成功,则会在target目录下生成一个jar包。如下图所示:

打包成功

打成了jar包后,该项目就可以在任何地方运行了!

**注:**在以前开发的web单体应用中,所有的功能被放在一个war包内;而使用Spring Boot开发的微服务项目中,各个项目会被打包成一个个可执行的jar包。

3.8、小结

简单几步,就完成了一个web接口的开发,SpringBoot就是这么简单。所以我们常用它来建立我们的微服务项目!

附:彩蛋——自定义banner图案

如何更改启动时显示的字符拼成的字母,也就是banner图案呢?

只需一步:在项目下的resources目录下新建一个banner.txt文件即可。

**注:**图案可以到网站:https://www.bootschool.net/ascii 生成,然后拷贝到文件中即可!

自定义banner

4、Spring Boot运行原理初探(重点)

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

4.1、pom.xml探究

4.1.1、父依赖

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

pom.xml文件:

1
2
3
4
5
6
7
<!-- 父项目 -->
<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文件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <!-- 资源过滤 -->
    <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文件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    <!-- 插件管理(部分) -->
    <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文件:

1
2
3
4
5
6
<!-- 父项目的父项目 -->
<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文件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    <!-- 依赖包版本管理(部分) -->
    <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项目中我们可以使用的所有依赖及其默认版本

4.1.2、启动器

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

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

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

pom.xml文件:

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

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

4.2、主启动类运行原理

4.2.1、默认的主启动类

Springboot01HelloworldApplication.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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进程。如下图所示:

SpringBoot应用进程

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

SpringBoot应用PID

4.2.2、@SpringBootApplication注解

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

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

SpringBootApplication.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@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 {
// ......
}

4.2.2.1、@ComponentScan注解

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

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

4.2.2.2、@SpringBootConfiguration注解

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

4.2.2.2.1、@Configuration注解

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

SpringBootConfiguration.java文件:

1
2
3
4
5
6
7
8
9
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
//配置类
@Configuration
@Indexed
public @interface SpringBootConfiguration {
// ......
}

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

4.2.2.2.2、@Component注解

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

Configuration.java文件:

1
2
3
4
5
6
7
8
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
//组件(Bean)
@Component
public @interface Configuration {
// ......
}

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

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

4.2.2.3、@EnableAutoConfiguration注解(重点)

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

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

4.2.2.3.1、@AutoConfigurationPackage注解

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

EnableAutoConfiguration.java文件:

1
2
3
4
5
6
7
8
9
10
11
@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文件:

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

注:

  • 这里的@import注解Spring的底层注解,它的作用是:向容器中导入组件(Bean)。

  • 这里的Registrar.class的作用是:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring IOC容器中。

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

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

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

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

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

AutoConfigurationImportSelector.java文件:

1
2
3
4
5
6
7
8
9
10
11
//获取候选的配置
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文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//自动配置核心文件的本地目录
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

WebMvcAutoConfiguration自动配置类

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

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

4.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)有了自动配置类,免去了我们手动编写配置注入功能组件等工作。

4.2.3、SpringApplication类

4.2.3.1、SpringApplication类的作用

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

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

4.2.3.2、run方法执行流程分析

跟着源码和这幅图就可以探究run方法执行流程了!

run方法执行流程

5、yaml配置文件

5.1、配置文件

SpringBoot使用一个全局的配置文件,配置文件名称是固定的

  • application.properties
    • 语法结构:key=value
    • 只能保存key=value键值对。
  • application.yml
    • 语法结构:key:空格 value
    • 既可以保存key: value键值对,又可以保存对象和数组。

**配置文件的作用:**修改SpringBoot自动配置的默认值。如:我们可以在配置文件中修改Tomcat默认启动的端口号等。

注:Spring Boot官方提供了可配置属性的参考文档

5.2、yaml概述

YAML"YAML Ain't a Markup Language"YAML不是一种标记语言)的递归缩写。

**YAML语言以数据为中心的标记语言!**它比XMLJSON更适合作为配置文件。

如:一个简单的端口配置。

  • 传统xml配置:
1
2
3
<server>
<port>8081<port>
</server>
  • yaml配置:
1
2
server:
prot: 8080

5.3、yaml基础语法

说明:yaml语法要求严格!

1、空格不能省略。

2、以缩进来控制层级关系,只要是左边对齐的一列数据都是同一个层级的。

3、属性和值的大小写都是十分敏感的。

(1)字面量:普通的值 [ 数字、布尔值、字符串 ]

语法格式:

1
key: value

注:

  • 字面量直接写在后面就可以,字符串默认不用加上双引号或者单引号。

  • “ ”双引号不会转义字符串里面的特殊字符 , 特殊字符会作为本身想表示的意思。

    如:name: "kuang \n shen" 输出:kuang 换行 shen

  • ''单引号,会转义特殊字符 , 特殊字符最终会变成和普通字符一样输出。

    如:name: ‘kuang \n shen’ 输出:kuang \n shen

(2)对象、Map(键值对)

语法格式:

1
2
3
4
#对象、Map格式(多行缩进写法)
ObjectName:
key1: value1
key2: value2

1
2
#对象、Map格式(行内写法)
ObjectName: {key1: value1,key2: value2}

如:

1
2
3
4
#多行缩进写法
student:
name: atang
age: 18
1
2
#行内写法
student: {name: atang,age: 18}

(3)数组(List、set)

语法格式:

1
2
3
4
5
#数组(多行缩进写法)
ArrayListName:
- value1
- value2
- value3

1
2
#数组(行内写法)
ArrayListName: [value1,value2,value3]

如:

1
2
3
4
5
#多行缩进写法
pets:
- cat
- dog
- pig
1
2
#行内写法
pets: [cat,dog,pig]

5.4、通过yaml配置文件注入类属性(重点)

yaml文件更强大的地方在于:它可以直接给我们的Java类【如:实体类pojo)、配置类xxxProperties)等】绑定起来,并注入匹配值!

**注:**实际项目中,我们常使用该方法给配置类注入值。

接下来,我们以实体类为例,介绍如何通过yaml配置文件来给Java类的属性赋值。

我们首先来回顾一下,原来是如何通过注解方式实体类注入匹配值的。

5.4.1、方式一:通过注解给实体类属性赋值(回顾)

(1)在springboot项目的pom.xml文件中导入lombok的依赖。

pom.xml文件:

1
2
3
4
5
6
7
<!-- lombok依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>

(2)在程序的主启动类的同级目录下新建一个pojo包,用于存放实体类。在其中编写一个Dog实体类,并使用@Value注解给bean注入属性值。

Dog.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.atangbiji.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

//将实体类设置成组件(bean),并注册到IOC容器中
@Component
//lombok
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dog {
@Value("旺财")
private String name;
@Value("3")
private Integer age;
}

(3)在SpringBoot的测试类中输出Dog对象。

Springboot01HelloworldApplicationTests.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.atangbiji;

import com.atangbiji.pojo.Dog;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

//SpringBoot单元测试
@SpringBootTest
class Springboot01HelloworldApplicationTests {

@Autowired //将Dog类自动注入进来
private Dog dog;

@Test
void contextLoads() {
//输出Dog对象
System.out.println(dog);
}
}

(4)运行测试函数,@Value注入成功,结果成功输出。如下图所示:

通过注解给实体类注入值

接下来,我们再来看看yaml文件是如何给实体类注入匹配值的。

5.4.2、方式二:通过yaml配置文件给实体类属性赋值(推荐)

(1)在springboot项目的resources根目录下新建一个application.yaml文件。

application.yaml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
person:
name: atang
age: 18
happy: true
birth: 2023/01/01
maps: {key1: val1,key2: val2}
lists:
- code
- music
- girl
dog:
name: 旺财
age: 3

**注:**我们还可以在yaml配置文件中使用占位符${}

(2)在pojo包下新建一个Person实体类。我们在Person类上使用**@ConfigurationProperties**注解加载全局的yaml配置文件注入属性的值。

Person.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.atangbiji.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Map;

//将实体类设置成组件(bean),并注册到IOC容器中
@Component
//通过配置文件配置属性值
@ConfigurationProperties(prefix = "person")
//lombok
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String name;
private Integer age;
private Boolean happy;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
}

其中:

  • @ConfigurationProperties的作用是:将配置文件中配置的每一个属性的值,映射到当前组件中;告诉SpringBoot将当前类中的所有属性和配置文件中相关的配置项进行绑定。
  • 参数prefix = “person”表示:将配置文件中person下面的所有属性与当前实体类进行绑定。

注:

  • @configurationProperties:默认从全局配置文件中获取值。
  • 我们也可以**@PropertySource注解和@Value**注解加载指定的配置文件中的值(详见5.4.3节)。
  • 当添加@ConfigurationProperties时,IDEA会提示:Spring Boot配置注解处理器**(Configuration Annotation Processor)**没有找到,并让我们查看官方文档。如下图所示:
配置注解处理器没有找到
  • 因为官方文档结构,点击查看文档可能会报404错误。仔细查看官方文档,可以找到配置文件处理器依赖!如下图所示:
配置文件处理器

(3)在springboot项目的pom.xml文件中导入配置文件处理器的依赖。

pom.xml文件:

1
2
3
4
5
6
<!-- 配置文件处理器依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

注:

  • 若导入配置文件处理器依赖,则配置文件进行绑定实体类就会有提示(需要重启)。
  • 若未导入配置文件处理器依赖,程序也可以运行,但IDEA会报红。

(4)在SpringBoot的测试类中输出Person对象。

Springboot01HelloworldApplicationTests.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.atangbiji;

import com.atangbiji.pojo.Person;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

//SpringBoot单元测试
@SpringBootTest
class Springboot01HelloworldApplicationTests {

@Autowired //将Person类自动注入进来
private Person person;

@Test
void contextLoads() {
//输出Person对象
System.out.println(person);
}
}

(5)运行测试函数,yaml配置文件中的属性与当前实体类绑定成功,结果成功输出。如下图所示:

yaml配置文件与实体类绑定成功

5.4.3、方式三:通过properties配置文件给实体类属性赋值

(1)在springboot项目的resources根目录下新建一个user.properties文件。

user.properties文件:

1
2
3
name= 张三
age=20
sex=

**注:**默认情况下,properties配置文件中的中文会乱码,我们需要将properties配置文件的默认编码方式设置为:UTF-8

编码设置

(2)在pojo包下新建一个User实体类。我们在User类上使用**@PropertySource注解加载指定的配置文件,并使用@Value注解和Spring表达式**(SpEL)注入属性的值。

User.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.atangbiji.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

//将实体类设置成组件(bean),并注册到IOC容器中
@Component
//加载指定的配置文件
@PropertySource(value = "classpath:user.properties")
//lombok
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
//使用@Value注解注入属性的值
@Value("${name}")
private String name;
//使用@Value注解和Spring表达式注入属性的值
@Value("#{9*2}")
private int age;
//使用@Value注解注入属性的值
@Value("${sex}")
private String sex;
}

**注:通过properties配置文件给实体类注入值需要在每一个属性上使用@Value**注解为其单独赋值,比较麻烦;而通过yaml配置文件给实体类注入值方则不需要。

(3)在SpringBoot的测试类中输出User对象。

Springboot01HelloworldApplicationTests.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.atangbiji;

import com.atangbiji.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

//SpringBoot单元测试
@SpringBootTest
class Springboot01HelloworldApplicationTests {

@Autowired //将User类自动注入进来
private User user;

@Test
void contextLoads() {
//输出User对象
System.out.println(user);
}
}

(4)运行测试函数,properties配置文件中的属性与当前实体类绑定成功,结果成功输出。如下图所示:

properties配置文件与实体类绑定成功

5.4.4、对比与小结

@ConfigurationProperties注解与@Value注解功能对比如下图所示:

注解对比

(1)@ConfigurationProperties注解只需要写一次即可; @Value注解则需要给每个字段都添加。

(2)什么是**松散绑定(又称:宽松绑定)**呢? 它是@ConfigurationProperties注解支持的格式,也是SpringBoot主推的做属性绑定的方式。如:我们的yml中写的last-name,和Java类中的lastName(驼峰命名)属性是一样的,-后面跟着的字母默认是大写的。这就是松散绑定。

(3)SpEL即:Spring表达式

(4)JSR303数据校验,这个就是我们可以在字段上增加一层过滤器验证,可以保证数据的合法性。

(5)复杂类型封装:yml中可以封装对象,使用value就不支持。

结论:

  • 通过yml配置文件和properties配置文件都可以注入Java类的属性值,强烈推荐yml
  • 若我们在某个业务中,只需要获取配置文件中的某个值,则可以使用一下@value注解。
  • 若我们专门编写了一个JavaBean来和配置文件进行一一映射,就直接使用@configurationProperties注解,不要犹豫!

6、JSR303数据校验

6.1、使用方法

JSR-303JAVA EE 6中的一项子规范,叫做Bean Validation(校验)。

Springboot中可以用@validated注解开启校验数据,若数据异常则会统一抛出异常,方便异常中心统一处理。

具体使用步骤如下:

(1)在上述Springboot项目的基础上,在pom.xml文件中导入JSR303数据校验(Validation)的启动器(依赖)。

pom.xml文件:

1
2
3
4
5
6
<!-- JSR303数据校验依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>3.0.1</version>
</dependency>

(2)使用@validated注解开启校验数据,并在Java类中进行具体数据校验。如:Person实体类。

Person.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.atangbiji.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
import java.util.Date;
import java.util.List;
import java.util.Map;

//将实体类设置成组件(bean),并注册到IOC容器中
@Component
@ConfigurationProperties(prefix = "person")
//lombok
@Data
@AllArgsConstructor
@NoArgsConstructor
//开启校验数据
@Validated
public class Person {
@NotNull//非空校验
private String name;
@Min(0)//最小值校验
@Max(150)//最大值校验
private Integer age;
private Boolean happy;
@Past//必须是一个过去的日期校验
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
}

(3)修改Person实体类绑定的yaml配置文件。

application.yaml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
person:
name: atang
age: 180 #超过最大值
happy: true
birth: 2123/01/01 #不是过去的时间
maps: {key1: val1,key2: val2}
lists:
- code
- music
- girl
dog:
name: 旺财
age: 3

(4)在SpringBoot的测试类中输出Person对象。

Springboot01HelloworldApplicationTests.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.atangbiji;

import com.atangbiji.pojo.Person;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

//SpringBoot单元测试
@SpringBootTest
class Springboot01HelloworldApplicationTests {

@Autowired //将Person类自动注入进来
private Person person;

@Test
void contextLoads() {
//输出Person对象
System.out.println(person);
}
}

(5)运行测试函数,后台输出错误并提示:yaml配置文件中的agebirth属性不满足要求。如下图所示:

数据校验结果

6.2、校验项

点击导入的javax.validation.constraints包,可以查看我们可以使用的所有校验项,如下图所示:

数据校验包

各校验项的详细信息和适用类型如下:

注解 详细信息 适用类型
@Null 被注解的字段必须为空
@NotNull 被注解的字段必须不为空
@NotBlank 带注解的元素不能为null,并且必须至少包含一个非空白字符
@NotEmpty 带注解的元素不能为null也不能为空 String(长度)集合(大小)数组(长度)
@AssertTrue 检查该字段必须为True Boolean
@AssertFalse 检查该字段必须为False Boolean
@Min(value) 被注解的字段必须大于等于指定的最小值
@Max(value) 被注解的字段必须小于等于指定的最大值
@Negative 带注解的元素必须是严格的负数(0被认为是无效值) BigDecimalBigIntegerbyteshortintlong及其包装类
@NegativeOrZero 带注解的元素必须是严格的负数或0 BigDecimalBigIntegerbyteshortintlong及其包装类
@Positive 带注解的元素必须是严格的正数(0被认为是无效值) BigDecimalBigIntegerbyteshortintlong及其包装类
@PositiveOrZero 带注解的元素必须是严格的正数或0 BigDecimalBigIntegerbyteshortintlong及其包装类
@DecimalMin 被注解的字段必须大于等于指定的最小值 BigDecimalBigIntegerbyteshortintlong及其包装类
@DecimalMax 被注解的字段必须小于等于指定的最大值 BigDecimalBigIntegerbyteshortintlong及其包装类
@Size(min=,max=) 被注解的字段的size必须在minmax之间,不需要判空 字符串、数组、集合
@Digits(integer, fraction) 被注解的字段必须在指定范围内,整数部分长度小于integer,小数部分长度小于fraction 字符串、数组、集合
@Past 被注解的字段必须是一个过去的日期时间
@PastOrPresent 被注解的字段必须是过去的或现在的日期时间
@Future 被注解的字段必须是一个将来的日期时间
@FutureOrPresent 被注解的字段必须是现在的或将来的日期时间
@Email 字符串必须是格式正确的电子邮件地址 String
@Pattern(value) 被注解的字段必须符合指定的正则表达式

7、多配置文件

7.1、配置文件加载位置

Spring Boot官方默认的配置文件加载位置有以下4个:

(1)file:./config/

即:项目路径(src的同级目录)下的config目录下。

(2)file:./

即:项目的根目录(src的同级目录)下。

(3)classpath:/config/

即:类路径(resources的同级目录)下的config目录下。

(4)classpath:/(默认)

即:类路径的根目录(resources的同级目录)下。

注:Spring Boot启动默认会扫描上述目录下的application.properties或者application.yml文件作为Spring boot的默认配置文件。

7.2、配置文件加载顺序

Spring Boot加载四个位置配置文件优先级为:

(1)file:./config/ >(2) file:./ > (3)classpath:/config/ > (4)classpath:/(默认)

注:

  • 高优先级的配置会覆盖低优先级的配置。
  • 相同的配置文件加载目录下,如果我们同时配置了yaml文件和properties文件,并且没有激活其它环境,那么默认会使用properties文件中的配置!

8、多环境切换

项目开发过程中往往需要有开发、生产、测试等多套环境。

profileSpring为我们提供的不同环境下激活不同配置的功能支持。我们可以通过激活不同的环境,快速实现环境的切换。具体实现方式如下:

8.1、方式一:配置多个配置文件(推荐)

(1)在配置文件加载目录下创建多个配置文件,并将它们命名为:application-{profile}.propertiesapplication-{profile}.yml(固定写法)。如:

  • application-dev.properties文件:代表开发环境配置
  • application-pro.properties文件:代表生产环境配置
  • application-test.properties文件:代表测试环境配置

但是Springboot默认并不会直接启用这些配置文件,它默认会使用application.properties主配置文件

(2)在主配置文件中选择当前需要激活的环境。

application.properties文件:

1
2
#通过Spring的profile激活开发环境配置
spring.profiles.active=dev

8.2、方式二:在同一个yaml中配置多文档块(不推荐)

使用yaml文件去实现多环境切换,不需要创建多个配置文件。我们只需在同一个yaml配置文件中配置多文档块即可配置多套环境。

(1)在yaml主配置文件中配置多文档块。如:

application.yaml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#文档块1
#选择要激活的环境块
spring:
profiles:
active: dev #激活开发环境
---
#文档块2
#配置服务器端口号
server:
port: 8081
#配置环境的名称:开发环境
spring:
profiles: dev

---
#文档块3
#配置服务器端口号
server:
port: 8082
#配置环境的名称:生产环境
spring:
profiles: pro

---
#文档块4
#配置服务器端口号
server:
port: 8083
#配置环境的名称:测试环境
spring:
profiles: test

**注:**各环境之间使用---进行分隔。

9、Spring Boot自动配置原理(重点)

配置文件到底能写哪些属性?怎么写?

SpringBoot官方文档中列出了所有可以配置的属性,但我们无法全部记住。如果掌握并理解了Spring Boot自动配置的原理,我们便可以通过查看源码的方式,知道Spring Boot到底有哪些属性可以配置,以及如何配置!进而以不变应万变!

9.1、分析过程

9.1.1、查看自动配置核心文件

在第4.2.2.3节中已经对主启动类上的@EnableAutoConfiguration注解进行了分析。并发现:Spring Boot自动装配其实是加载了本地spring-boot-autoconfigure-2.7.7.jar包的META-INF/spring目录下的自动配置核心文件(即org.springframework.boot.autoconfigure.AutoConfiguration.imports文件)。

接下来,我们以**HttpEncodingAutoConfigurationHttp编码自动配置类)**为例解释自动配置原理。

9.1.2、全局搜索自动配置类

全局搜索HttpEncodingAutoConfiguration自动配置类

HttpEncodingAutoConfiguration.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//表示这是一个自动配置类,和以前Spring中编写的xml配置文件一样,也可以向IOC容器中添加组件。
@AutoConfiguration
//启动指定类的属性配置功能(参数:xxxProperties类为属性配置类)
@EnableConfigurationProperties(ServerProperties.class)
//自动配置类生效的条件
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {

private final Encoding properties;

public HttpEncodingAutoConfiguration(ServerProperties properties) {
this.properties = properties.getServlet().getEncoding();
}
//向容器中添加一个组件,这个组件的某些值需要从属性配置类中获取。
//即:从配置文件中获取指定的值和bean的属性进行绑定
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
return filter;
}
//向容器中添加一个组件,这个组件的某些值需要从属性配置类中获取
@Bean
public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
return new LocaleCharsetMappingsCustomizer(this.properties);
}

// ......
}

可以发现:这些类的都是JavaConfig自动配置类,而且都注入了一些Bean。其中:

(1)@AutoConfiguration注解:表示这是一个自动配置类,和以前Spring中编写的xml配置文件一样,也可以向IOC容器中添加组件。

(2)@EnableConfigurationProperties(ServerProperties.class)注解:表示启动指定类的属性配置功能。

点击进入ServerProperties类,可以发现:

  • xxxProperties就是一个属性配置类并通过配置文件的方式给该类中的属性配置值(详见第5.4.2节)。

ServerProperties.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//通过配置文件配置属性值(参数prefix = “server”表示:将配置文件中server下面的所有属性与当前实体类进行绑定。)
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {

//可以通过配置文件配置的属性
private Integer port;

private InetAddress address;

@NestedConfigurationProperty
private final Compression compression = new Compression();

@NestedConfigurationProperty
private final ErrorProperties error = new ErrorProperties();

private ForwardHeadersStrategy forwardHeadersStrategy;

// ......

public Integer getPort() {
return this.port;
}

public void setPort(Integer port) {
this.port = port;
}

public InetAddress getAddress() {
return this.address;
}

public void setAddress(InetAddress address) {
this.address = address;
}

public ErrorProperties getError() {
return this.error;
}

public Compression getCompression() {
return this.compression;
}

public ForwardHeadersStrategy getForwardHeadersStrategy() {
return this.forwardHeadersStrategy;
}

public void setForwardHeadersStrategy(ForwardHeadersStrategy forwardHeadersStrategy) {
this.forwardHeadersStrategy = forwardHeadersStrategy;
}

// ......

}
  • propertiesyaml配置文件中可以配置的属性与上述属性配置类中的属性完全一致!如下图所示:
properties配置文件提示 yaml配置文件提示

(3)@ConditionalOnXXX注解:表示自动配置类生效的条件。它们的底层都包含@Conditional注解。其中:

  • @ConditionalOnWebApplication(type = ConditionalOnWebApplicatiweb应用,如果是,则当前配置类生效。

  • @ConditionalOnClass(CharacterEncodingFilter.class)注解表示:判断当前项目有没有SpringMVC中进行乱码解决的过滤器。

  • @ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)注解表示:判断配置文件中是否存在某个配置(spring.servlet.encoding.enabled)。即使配置文件中不配置spring.servlet.encoding.enabled=true,也是默认生效的。

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

(4)自动配置类的作用就是:向容器中添加组件,且这些组件的某些值需要从属性配置类中获取。即:从(propertiesyaml)配置文件中获取指定的值和Bean的属性进行绑定。

这就是Spring Boot自动装配的原理!

9.1.3、@Conditional注解

9.1.3.1、含义

@Conditional注解表示:自动配置类必须在一定的条件下才能生效。

9.1.3.2、派生注解

@Conditional派生注解及其作用,如下图所示:

派生注解

9.1.3.3、输出自动配置报告

Spring Boot那么多默认的自动配置类,每一个都必须在一定的条件下才能生效。也就是说,我们加载了这么多的自动配置类,但并不是所有的都生效了。

我们怎么知道哪些自动配置类生效了呢?

我们可以在配置文件中启用debug=true属性,在控制台输出自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效了。

application.properties文件:

1
2
#开启springboot的调试类
debug=true

运行主启动类,输出结果如下:

自动配置生效 自动配置未生效 自动配置无条件

注:

  • Positive matches:(自动配置类启用,即:正匹配)

  • Negative matches:(自动配置类未启用,没有匹配成功,即:负匹配)

  • Unconditional classes:(没有条件的自动配置类)

9.2、结论

  • propertiesyaml配置文件中可以配置的属性就是所有自动配置类(xxxAutoConfiguration)对应的**属性配置类(xxxProperties)**中的属性!
  • 自动配置真正实现是从classpath中搜寻所有META-INF/spring/目录下的org.springframework.boot.autoconfigure.AutoConfiguration.imports配置文件,并将其中对应的配置项,通过反射实例化为自动配置类,然后将这些都汇总成为一个实例并加载到Spring IOC容器中。
  • 只有**@ConditionalOnXXX注解**中的条件都满足时,自动配置类及其配置的相应组件(Bean)才会生效。
  • 如果某个自动配置类生效,那么该配置类就会向容器中添加各种组件。
  • 这些组件的属性是从对应的属性配置类(xxxProperties)中获取的,这些属性配置类中的每一个属性又是和(propertiesyaml)配置文件绑定的。
  • 所有在配置文件中能配置的属性都封装在属性配置类(xxxxProperties)类中。
  • 配置文件能配置什么就可以参照某个功能对应的这个属性类(先找到对应的自动配置类,再找到对应的属性配置类)。

9.3、精髓(重点)

Spring Boot自动配置原理的精髓如下图所示:

自动配置原理

(1)SpringBoot在启动时会加载大量的自动配置类。

(2)我们需要查看SpringBoot默认写好的自动配置类当中是否存在我们需要的功能。(若不存在,则需要我们手动配置)

(3)若存在,则我们再来看这个自动配置类中到底配置了哪些组件(只要我们要用的组件存在在其中,我们就不需要再手动配置了)。

(4)自动配置类向容器中添加组件的时候,会从属性配置类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可!

注:Spring Boot自动配置的两个核心类:

  • xxxAutoConfigurartion:自动配置类

    • 作用:向容器中添加组件。
    • 自动配置类添加的这些组件的某些值需要从属性配置类中获取。即:从(propertiesyaml)配置文件中获取指定的值和Bean的属性进行绑定。
  • xxxxProperties:属性配置类

    • 作用:配置自动配置类添加的这些组件的某些参数的值。即:封装(propertiesyaml)配置文件的内容,供自动配置类使用。
    • 通过(propertiesyaml)配置文件的方式给属性配置类中的属性配置值。

10、自定义启动器

我们分析完源码并理解自动装配的原理后,我们可以尝试自定义一个启动器来玩玩!

10.1、命名归约

(1)官方命名

  • 前缀:spring-boot-starter-xxx
  • 比如:spring-boot-starter-web....

(2)自定义命名

  • 后缀:xxx-spring-boot-starter
  • 比如:mybatis-spring-boot-starter

10.2、编写启动器

(1)在IDEA中新建一个项目名称为:spring-boot-starter-diy的空项目,将其作为父项目。

空项目

(2)点击项目结构按钮,在父项目中新建一个模块(Module),并在其中创建一个普通的Maven子项目,项目名称为atang-spring-boot-starter

新建启动器项目

**注:**启动器模块是一个空jar模块,仅提供辅助性依赖管理,这些依赖可能用于自动装配或者其他类库。

(3)再在父项目中新建一个模块(Module),并在其中创建一个项目名称为atang-spring-boot-starter-autoconfigureSpring Initializr项目。

新建自动配置项目
  • 选择SpringBoot 2.7.7后,不选择任何初始化的组件,直接下一步至Finish即可。

选择SpringBoot版本

  • 此时,项目结构如下图所示:
项目结构

(4)在我们自定义的启动器中导入autoconfigure的依赖!

pom.xml文件:

1
2
3
4
5
6
7
8
9
<!-- 自定义启动器依赖 -->
<dependencies>
<!-- 引入自动配置模块 -->
<dependency>
<groupId>com.atangbiji</groupId>
<artifactId>atang-spring-boot-starter-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>

(5)修改自定义的自动配置模块。

  • 先删除autoconfigure项目下IDEA自动生成的.mvn文件夹、.gitignoreHELP.mdmvnwmvnw.cmd文件。
  • 再删除Spring Boot项目默认生成的AtangSpringBootStarterAutoconfigureApplication.java文件、application.properties文件以及test文件夹。
  • pom.xml文件中只留下一个spring-boot-starter,这是所有的启动器基本配置!

此时,项目结构如下图所示:

删除后项目结构

pom.xml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- 父项目(注意版本号) -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!-- Maven项目的GAV(坐标) -->
<groupId>com.atangbiji</groupId>
<artifactId>atang-spring-boot-starter-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>atang-spring-boot-starter-autoconfigure</name>
<description>Demo project for Spring Boot</description>
<properties>
<!-- 注意Java版本 -->
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- 配置文件处理器依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

</project>

注:

  • 记得选择Spring Boot 2.7.7版本,否则IDEA默认创建Spring Boot 3.0.1项目,生成的父项目(spring-boot-starter-parent)的版本号和Java版本号可能存在问题,记得修正。
  • Maven未识别到该子项目,则右键为其添加Maven支持。
添加Maven支持

(6)先将javaresources文件夹分别标记为源文件和资源文件的根目录。然后在com.atangbiji包下新建一个HelloService类,用于编写自己的服务。

HelloService.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.atangbiji;

//自定义服务
public class HelloService {

//自定义服务中使用属性配置类
HelloProperties helloProperties;

public HelloProperties getHelloProperties() {
return helloProperties;
}

public void setHelloProperties(HelloProperties helloProperties) {
this.helloProperties = helloProperties;
}

//自定义接口
public String sayHello(String name){
return helloProperties.getPrefix() + name + helloProperties.getSuffix();
}
}

(7)在com.atangbiji包下新建一个HelloProperties属性配置类

HelloProperties.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.atangbiji;

import org.springframework.boot.context.properties.ConfigurationProperties;

//自定义属性配置类
//前缀:atang.hello
@ConfigurationProperties(prefix = "atang.hello")
public class HelloProperties {

private String prefix;
private String suffix;

public String getPrefix() {
return prefix;
}

public void setPrefix(String prefix) {
this.prefix = prefix;
}

public String getSuffix() {
return suffix;
}

public void setSuffix(String suffix) {
this.suffix = suffix;
}
}

注:当添加@ConfigurationProperties时,IDEA会提示:Spring Boot配置注解处理器(Configuration Annotation Processor)没有找到,并让我们查看官方文档。(参见第5.4.2节)此时,需要在Spring Boot项目的pom.xml文件中导入配置文件处理器的依赖。

pom.xml文件:

1
2
3
4
5
6
<!-- 配置文件处理器依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

(8)在com.atangbiji包下新建一个HelloServiceAutoConfiguration自动配置类,并注入Bean

HelloServiceAutoConfiguration.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.atangbiji;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

//自定义自动配置类
@Configuration
//当项目是web应用时才生效
@ConditionalOnWebApplication
//让HelloProperties属性配置类生效
@EnableConfigurationProperties(HelloProperties.class)
public class HelloServiceAutoConfiguration {

@Autowired
HelloProperties helloProperties;//属性配置类

//向容器中添加一个组件,这个组件的某些值需要从属性配置类中获取。
//即:从配置文件中获取指定的值和bean的属性进行绑定
@Bean
public HelloService helloService(){
HelloService service = new HelloService();
service.setHelloProperties(helloProperties);
return service;
}
}

(9)在resources目录下新建一个META-INF\spring目录,并在该目录下编写一个自己的org.springframework.boot.autoconfigure.AutoConfiguration.import文件。

org.springframework.boot.autoconfigure.AutoConfiguration.import文件:

1
com.atangbiji.HelloServiceAutoConfiguration

**注:**该文件为Text文本格式。

设置文件格式

(10)编写完成后,把自己编写的jar包安装到maven仓库中!

安装jar包

注:

  • Maven可以通过install命令将自己编写的jar文件安装到自己的本地仓库中 。

  • 安装成功后,target目录下生成相应的jar包。如下图所示:

安装jar包成功

10.3、测试

(1)在父项目中新建一个模块(Module),并在其中创建一个项目名称为atang-start-testSpring Initializr(版本:2.7.7Web项目,用于测试我们自己编写的启动器。

启动器测试项目 启动器测试Web项目

注:SpringBoot选择2.7.7版本,并选择Spring Web作为初始化组件。

(2)先删除IDEA自动生成的.mvn文件夹、.gitignoreHELP.mdmvnwmvnw.cmd文件。

(3)在该子项目的pom.xml文件中导入我们自己编写的启动器。

pom.xml文件:

1
2
3
4
5
6
<!--自定义启动器-->
<dependency>
<groupId>com.atangbiji</groupId>
<artifactId>atang-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>

(4)在程序的主启动类(AtangStartTestApplication)的同级目录下新建一个controller包,用于存放控制器代码。并在其中编写一个HelloController类,用于测试我们自己编写的接口。

HelloController.java文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.atangbiji.controller;

import com.atangbiji.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

//控制器
@RestController
public class HelloController {
@Autowired
HelloService helloService;

@RequestMapping("/hello")
public String hello() {
return helloService.sayHello("自定义启动器测试");
}
}

**注:**若Maven未识别到该子项目,则右键为其添加Maven支持。

(5)编写application.properties配置文件。

application.properties文件:

1
2
atang.hello.prefix="aaa"
atang.hello.suffix="zzz"

(6)启动主启动类中的main函数,在浏览器中输入http://localhost:8080/hello,页面访问成功。访问结果如下图所示:

自定义启动器访问成功

温馨提示:学完的东西一定要多下去实践!

(持续更新中…… )

阿汤笔迹微信公众平台

关注**“阿汤笔迹”** 微信公众号,获取更多学习笔记。
原文地址:http://www.atangbiji.com/2023/01/15/SpringBootInDetail01Core
博主最新文章在个人博客 http://www.atangbiji.com/ 发布。