SpringBoot入门及原理

1. SpringBoot简介

1.1 Spring回顾

回顾:什么是Spring

Spring 框架是一个开放源代码的 J2EE 应用程序框架,由 Rod Johnson 发起,是针对 bean 的生命周期进行管理的轻量级容器。

Spring 框架是由于软件开发的复杂性而创建的。为了解决企业级应用开发的复杂性、简化开发。

Spring是如何简化Java开发的

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

  1. 基于POJO的轻量级和最小侵入性编程,所有东西都是bean
  2. 通过IOC,依赖注入(DI)和面向接口实现松耦合
  3. 基于切面(AOP)和惯例进行声明式编程
  4. 通过切面和模版减少样式代码,RedisTemplate,xxxTemplate

1.2 SpringBoot概述

什么是SpringBoot

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。

特点:

SpringBoot基于Spring4.0设计,不仅继承了Spring框架原有的优秀特性,而且还通过简化配置来进一步简化了Spring应用的整个搭建和开发过程。另外SpringBoot通过集成大量的框架使得依赖包的版本冲突,以及引用的不稳定性等问题得到了很好的解决。

SpringBoot所具备的特征有:

  1. 可以创建独立的Spring应用程序,并且基于其Maven或Gradle插件,可以创建可执行的JARs和WARs
  2. 内嵌Tomcat或Jetty等Servlet容器
  3. 提供自动配置的“starter”项目对象模型(POMS)以简化Maven配置
  4. 尽可能自动配置Spring容器
  5. 提供准备好的特性,如指标、健康检查和外部化配置
  6. 绝对没有代码生成,不需要XML配置

重要策略:

SpringBoot框架中还有两个非常重要的策略:开箱即用和约定优于配置。

开箱即用,Outofbox,是指在开发过程中,通过在MAVEN项目的pom文件中添加相关依赖包,然后使用对应注解来代替繁琐的XML配置文件以管理对象的生命周期。这个特点使得开发人员摆脱了复杂的配置工作以及依赖的管理工作,更加专注于业务逻辑。

约定优于配置,Convention over configuration,是一种由SpringBoot本身来配置目标结构,由开发者在结构中添加信息的软件设计范式。这一特点虽降低了部分灵活性,增加了BUG定位的复杂性,但减少了开发人员需要做出决定的数量,同时减少了大量的XML配置,并且可以将代码编译、测试和打包等工作自动化。

1.3 微服务架构

微服务

种软件开发技术- 面向服务的体系结构(SOA)架构样式的一种变体,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相沟通(通常是基于HTTP的RESTful API)。每个服务都围绕着具体业务进行构建,并且能够独立地部署到生产环境、类生产环境等。另外,应尽量避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据上下文,选择合适的语言、工具对其进行构建。

单体应用架构

将所有的业务场景的表示层,业务逻辑层和数据访问层打包放在一个工程中,最终经过编译打包,部署在一台服务器上。例如:典型的J2EE工程,把jsp,业务逻辑层的service,controller和数据访问层的dao,打包成wa包,部署在Tomcat或者Jetty或者其他容器上运行。

优点:

  • 部署简单 :由于是完整的结构体,可以直接部署在一份服务器上即可
  • 技术单一 :项目不需要复杂的技术栈,往往一套熟悉的技术栈就可以完成开发
  • 用人成本低 :单个程序员可以完成业务接口道数据库的整个流程

缺点:

  • 业务越来越复杂,单体架构扩展性不足,业务扩展带来的代价越来越大;
  • 用户越来越多,程序承受的并发越爱越高,单体应用的并发能力有限;
  • 单体应用的业务都在同一个程序中,增删改业务修改,也会影响其他代码,给测试增加了难度

微服务架构

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

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

优点:

  • 节省调用资源
  • 每个功能元素的服务都是一个可替代的、可独立升级的软件代码。

微服务架构

相关论文:

2. 第一个SpringBoot程序

方式1:官网下载jar包,IEDA导入(不推荐)

官网:https://start.spring.io/

注意导入Spring Web依赖

QQ截图20221001172400

……

方式2:IDEA创建(社区版IDEA插件收费,呜呜呜~😭,免费试用一个月)

  1. 新建项目

    QQ截图20221001171432

    QQ截图20221001171452

    项目创建成功!!!

    一个空的 SpringBoot 项目

    QQ截图20221001173222

  2. 项目结构分析

    项目创建完成后,会自动生成以下文件:

    • 程序的主程序类
    • 一个 application.properties 配置文件
    • 一个测试类

    生成的 DemoApplication 和测试包下的 DemoApplicationTests 类都可以直接运行来启动当前创建的项目,由于该项目未配任何数据访问或 Web 模块,程序会在加载完 Spring 之后结束运行。

    QQ截图20221001180846

  3. pom.xml 分析

    SpringBoot项目依赖: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
    <?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.4</version>
    <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.study</groupId>
    <artifactId>HelloWorld</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>HelloWorld</name>
    <description>HelloWorld</description>
    <properties>
    <java.version>11</java.version>
    </properties>
    <!--依赖-->
    <dependencies>
    <!--启动器-->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <!--web依赖:集成了tomcat,替代了dispatcherServlet,xml配置...-->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--单元测试,等价于junit-->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>
    </dependencies>

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

    </project>

    主要部分:

    • 项目元数据信息:项目时候输入的Project Metadata部分,也就是Maven项目的基本元素,包括:groupId、artifactId、version、name、description
    • parent:继承 spring-boot-starter-parent 的依赖管理,控制版本与打包等内容。
    • dependencies:项目具体依赖,包含了 spring-boot-starter-web 用于实现 HTTP 接口(该依赖中包含了 Spring MVC)。官网描述:使用 Spring MVC 构建 Web (包括RestFul)应用程序的入门者,使用 Tomcat 作为默认嵌入式容器。spring-boot-starter-test:用于编写单元测试的依赖包。
    • build:构建配置部分,默认使用 spring-boot-maven-plugin ,配合 spring-boot-starter-parent 就可以把SpringBoot项目打包成 jar 包直接运行。
  4. 编写HTTP接口

    • 同级目录建包:新建一个 controller 包

    • 在包中编写 Controller 类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      package com.study.helloworld.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,SpringBoot!";
      }
      }
    • 浏览器访问

      测试结果:

      QQ截图20221001175708

  5. 将SpringBoot打成jar包

    将 SpringBoot 打成jar包就可以在任何地方运行

    QQ截图20221001183314

    运行jar包

    …….

3. 运行原理

SpringBoot 自动装配原理 —- SpringBoot启动原理

3.1 pom.xml

父依赖

主要是依赖一个父项目,主要是管理项目的资源过滤及插件。

1
2
3
4
5
6
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

点进去,发现还有一个父依赖

  • spring-boot-dependencies:核心依赖在父工程中,SpringBoot的版本控制中心
  • 我们在写或者引入SpringBoot依赖时不需要指定版本,就是因为有这些版本仓库
1
2
3
4
5
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.7.4</version>
</parent>

启动器

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
  • springboot-boot-starter-xxx:spring-boot的场景启动器

  • 如spring-boot-starter-web:自动导入web环境所有的依赖

  • SpringBoot将所有的功能场景都抽取出来,变成一个个的启动器,只需要在项目中引入这些 starter即可,所有相关的依赖都会导入进来。

  • 要用什么功能就导入什么样的场景启动器即可

3.2 主启动类

默认的主启动类

1
2
3
4
5
6
7
8
9
10
//本身就是Spring的一个组件
//程序的主入口
@SpringBootApplication
public class HelloWorldApplication {

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

}

@SpringBootApplication

作用:标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
// ......
}

@ComponentScan

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

作用:自动扫描并加载符合条件的组件或者bean,将这个bean定义加载到IOC容器中

@SpringBootConfiguration

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

进入@SpringBootConfiguration注解查看

1
2
3
4
5
// 点进去得到下面的 @Component
@Configuration
public @interface SpringBootConfiguration {}
@Component
public @interface Configuration {}

@Configuration:说明这是一个配置类,配置类就是对应Spring的xml 配置文件

@Component:说明启动类本身也是Spring中的一个组件,负责启动应用。

@EnableAutoConfiguration

@EnableAutoConfiguration:开启自动配置功能

以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置,@EnableAutoConfiguration 告诉SpringBoot开启自动配置功能,这样自动配置才能生效。

@AutoConfigurationPackage:自动配置包

1
2
3
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}

@import:Spring底层注解@import,给容器中导入一个组件

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

@Import({AutoConfigurationImportSelector.class}) :给容器导入组件

AutoConfigurationImportSelector:自动配置导入选择器

  1. 这个类中有一个这样的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 获得候选的配置
    protected List<String> getCandidateConfigurations(AnnotationMetadata
    metadata, AnnotationAttributes attributes) {
    //这里的getSpringFactoriesLoaderFactoryClass()方法
    //返回的就是我们最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration
    List<String> configurations =
    SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryCl
    ass(), this.getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
    return configurations;
    }
  2. 这个方法又调用了 SpringFactoriesLoader 类的静态方法,进入SpringFactoriesLoader类 loadFactoryNames() 方法

    1
    2
    3
    4
    5
    6
    7
    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable
    ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    //这里它又调用了 loadSpringFactories 方法
    return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName,
    Collections.emptyList());
    }
  3. 继续点击查看 loadSpringFactories 方法

    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
    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    //获得classLoader , 我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身
    MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
    if (result != null) {
    return result;
    } else {
    try {
    //去获取一个资源 "META-INF/spring.factories"
    Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
    LinkedMultiValueMap result = new LinkedMultiValueMap();

    //将读取到的资源遍历,封装成为一个Properties
    while(urls.hasMoreElements()) {
    URL url = (URL)urls.nextElement();
    UrlResource resource = new UrlResource(url);
    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
    Iterator var6 = properties.entrySet().iterator();
    while(var6.hasNext()) {
    Entry<?, ?> entry = (Entry)var6.next();
    String factoryClassName = ((String)entry.getKey()).trim();
    String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
    int var10 = var9.length;
    for(int var11 = 0; var11 < var10; ++var11) {
    String factoryName = var9[var11];
    result.add(factoryClassName, factoryName.trim());
    }
    }
    }
    cache.put(classLoader, result);
    return result;
    } catch (IOException var13) {
    throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
    }
    }
    }
  4. 发现一个多次出现的文件:spring.factories,全局搜索它

spring.factories

我们根据源头打开spring.factories , 看到了很多自动配置的文件;这就是自动配置根源所在。

结论:自动配置真正实现是从 classpath 中搜寻所有的 META-INF/spring.factories 配置文件,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化为对应标注了 @Configuration 的JavaConfig形式的IOC容器配置类,然后将这些都汇总成为一个实例并加载到IOC容器中。

结论:

  1. SpringBoot在启动的时候从类路径下的 META-INF/spring.factories 中获取 EnableAutoConfiguration 指定的值。
  2. 将这些值作为自动配置类导入容器,自动配置类就生效,帮我们进行自动配置工作。
  3. 整个J2EE的整体解决方案和自动配置都在 springboot-autoconfigure 的 jar 包中。
  4. 它会给容器中导入非常多的自动配置类(xxxAutoConfiguration),就是给容器中导入这个场景需要的所有组件,并配置好这些组件。
  5. 有了自动配置类,免去了手动编写配置注入功能组件等的工作。

3.3 SpringApplication

不简单的方法

SpringApplication开启了一个服务

1
2
3
4
5
6
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}

SpringApplication.run 分析,分析该方法主要分两部分:一部分是SpringApplication的实例化,二是run方法的执行

SpringApplication

这个类主要做了以下四件事情:

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

查看构造器:

1
2
3
4
5
6
7
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
// ......
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.setInitializers(this.getSpringFactoriesInstances();
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}

run方法

IMG_20221004_163853

4. yaml语法

4.1 配置文件

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

  • application.properties
    • 语法结构:key=value
  • application.yml
    • 语法结构 :key:空格 value

配置文件的作用:修改SpringBoot自动配置的默认值,因为SpringBoot在底层都给我们自动配置好了。

4.2 yaml概述

YAML是”YAML Ain’t a Markup Language”(YAML不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:”Yet Another Markup Language”(仍是一种标记语言),但为了强调这种语言以数据做为中心,而不是以标记语言为重点,而用反向缩略语重命名。

  • yaml 格式可以保存键值对、对象、数组… properties 格式只能保存键值对

  • 以前配置文件大多数使用 xml 配置,对比 yaml 和 xml

    传统xml配置:

    1
    2
    3
    <server>
    <port>8081<port>
    </server>

    yaml配置:

    1
    2
    server:
    prot: 8080

4.3 yml基础语法

yaml 格式对空格的要求非常高

  1. 空格不能省略
  2. 以缩进来控制层级关系,只要是左边对齐的一列数据都是同一个层级的。
  3. 属性和值的大小写都是十分敏感的。

普通 key-value

1
2
server:
port: 8081

对象

1
2
3
student:
name: 林慕椿
age: 18

行内写法:

1
student: {name: 林慕椿,age: 18}

数组

1
2
3
4
animal:
- cat
- dog
- pig

行内写法:

1
animal: [cat,dog,pig]

5. 注入配置文件

5.1 yaml注入配置文件

yaml文件更强大的地方在于可以给实体类直接注入匹配值

原注入配置文件方法

  1. 在springboot项目中的 resources 目录下新建一个文件 application.yml

  2. 编写一个实体类 Dog

    1
    2
    3
    4
    5
    6
    7
    8
    import org.springframework.stereotype.Component;

    @Component //注册bean到容器中
    public class Dog {
    private String name;
    private Integer age;
    //有参无参构造、get、set方法、toString()方法
    }
  3. 使用 @Value 注解给bean注入属性值

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

    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;

    @Component //注册bean到容器中
    public class Dog {
    @Value("汪汪")
    private String name;
    @Value("3")
    private Integer age;
    //有参无参构造、get、set方法、toString()方法
    }
  4. 在SpringBoot的测试类下注入Dog后测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @SpringBootTest
    class HelloWorldApplicationTests {

    @Autowired //自动装配
    private Dog dog;
    @Test
    void contextLoads() {
    System.out.println(dog);
    }

    }

    测试结果:

    Dog{name=’汪汪’, age=3}

    结果成功输出,@Value注入成功,这是原来的注入方法

yaml注入配置文件

  1. 编写一个复杂一点的实体类:Person 类

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

    import org.springframework.stereotype.Component;

    import java.util.Date;
    import java.util.List;
    import java.util.Map;

    @Component //注册bean到容器中
    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;
    //有参无参构造、get、set方法、toString()方法
    }
  2. 使用yaml配置的方式进行注入

    编写一个yaml配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    person:
    name: 林慕椿
    age: 18
    happy: yes
    birth: 2002/02/02
    maps: {k1: v1,k2: v2}
    lists:
    - music
    - code
    - study
    dog:
    name: 汪汪
    age: 3
  3. 将yaml配置文件注入到类中

    **@ConfigurationProperties(prefix = “person”)**作用:将配置文件中配置的每一个属性的值,映射到这个组件中,告诉SpringBoot将本类中的所有属性和配置文件中相关配置进行绑定。

    参数 prefix = “person” :将配置文件中的person下面的所有属性一一对应

    只有这个组件是容器中的组件,才能使用容器提供的@ConfigurationProperties功能

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

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

    import java.util.Date;
    import java.util.List;
    import java.util.Map;

    @Component //注册bean到容器中
    @ConfigurationProperties(prefix = "person")
    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;
    //有参无参构造、get、set方法、toString()方法
    }
  4. 测试类中测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @SpringBootTest
    class HelloWorldApplicationTests {

    @Autowired //自动装配
    private Person person;
    @Test
    void contextLoads() {
    System.out.println(person);
    }

    }

    测试结果:

    Person{name=’林慕椿’, age=18, happy=true, birth=Sat Feb 02 00:00:00 CST 2002, maps={k1=v1, k2=v2}, lists=[music, code, study], dog=Dog{name=’汪汪’, age=3}}

yaml配置文件占位符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
person:
name: linmuchun${random.uuid} # 随机uuid
age: ${random.int} # 随机int
happy: false
birth: 2002/02/02
maps: {k1: v1,k2: v2}
lists:
- code
- girl
- music
dog:
# 引用person.hello 的值,如果不存在就用 :后面的值,即 other,然后拼接上_旺财
name: ${person.hello:other}_旺财
age: 1

5.2 properties注入配置文件

加载指定配置文件

@PropertySource:加载指定的配置文件

@ConfigurationProperties:默认从全局配置文件中获取值

  1. 在resources目录下新建一个person.properties文件

    1
    name=林慕椿
  2. 在代码中指定加载person.properties文件

    1
    2
    3
    4
    5
    6
    7
    @PropertySource(value = "classpath:person.properties")
    @Component //注册bean
    public class Person {
    @Value("${name}")
    private String name;
    ......
    }
  3. 再次输出测试一下:指定配置文件绑定成功

properties注入配置文件

yaml方法都是最简单的方式,开发中最常用的,也是springboot所推荐的。

其他实现方式:配置文件除了yml还有properties

测试步骤:

  1. 新建一个实体类User

    1
    2
    3
    4
    5
    6
    @Component //注册bean
    public class User {
    private String name;
    private int age;
    private String sex;
    }
  2. 编辑配置文件 user.properties

    1
    2
    3
    user1.name=kuangshen
    user1.age=18
    user1.sex=
  3. 在User类上使用@Value来进行注入

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Component //注册bean
    @PropertySource(value = "classpath:user.properties")
    public class User {
    //直接使用@value
    @Value("${user.name}") //从配置文件中取值
    private String name;
    @Value("#{9*2}") // #{SPEL} Spring表达式
    private int age;
    @Value("男") // 字面量
    private String sex;
    }
  4. SpringBoot测试

对比

功能对比图:

@ConfigurationProperties @Value
功能 批量注入配置文件中的属性 一个个指定
松散绑定(松散语法) 支持 不支持
SpEL 不支持 支持
JSR303数据校验 支持 不支持
复杂类型封装 支持 不支持
  1. @ConfigurationProperties只需要写一次即可,@Value则需要每个字段都添加
  2. 松散绑定:yml配置文件中写的last-name和lastName是一样的,- 后面跟着的字母默认是大写的。
  3. JSR303数据校验,这个就是可以在字段是增加一层过滤器验证,可以保证数据的合法性
  4. 复杂类型封装,yml中可以封装对象,使用value就不支持

结论:

  • 配置yml和配置properties都可以获取到值(建议yaml)
  • 如果在某个业务中,只需要获取配置文件中的某个值,可以使用 @value
  • 如果专门编写了一个JavaBean来和配置文件进行一一映射,就直接使用@ConfigurationProperties

5.3 JSR303数据校验

SpringBoot中可以用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。

例:添加注解让 name 只能支持 Email 格式。

1
2
3
4
5
6
7
@Component //注册bean
@ConfigurationProperties(prefix = "person")
@Validated //数据校验
public class Person {
@Email(message="邮箱格式错误") //name必须是邮箱格式
private String name;
}

运行结果 : default message [不是一个合法的电子邮件地址]

使用数据校验,可以保证数据的正确性,下面列出一些常见的使用:

1
2
3
4
5
6
@NotNull(message="名字不能为空")
private String userName;
@Max(value=120,message="年龄最大不能查过120")
private int age;
@Email(message="邮箱格式错误")
private String email;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
空检查
@Null 验证对象是否为null
@NotNull 验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格
@NotEmpty 检查约束元素是否为NULL或者是EMPTY

Booelan检查
@AssertTrue 验证 Boolean 对象是否为 true
@AssertFalse 验证 Boolean 对象是否为 false

长度检查
@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Length(min=, max=) string is between min and max included

日期检查
@Past 验证 Date 和 Calendar 对象是否在当前时间之前
@Future 验证 Date 和 Calendar 对象是否在当前时间之后
@Pattern 验证 String 对象是否符合正则表达式的规则

......
除此以外,还可以自定义一些数据校验规则

6. 多环境切换

6.1 多环境切换

profile是Spring对不同环境提供不同配置功能的支持,可以通过激活不同的环境版本,实现快速切换环境。

方式1:多配置文件

在主配置文件编写的时候,文件名可以是 application-{profile}.properties/yml , 用来指定多个环境版本。

例如:

application-test.properties 代表测试环境配置

application-dev.properties 代表开发环境配置

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

需要通过一个配置来选择需要激活的环境:

1
2
3
4
#比如在配置文件中指定使用dev环境
#springboot的多环境配置,可以选择激活哪一个配置
spring.profiles.active=dev
#我们启动SpringBoot,就可以看到已经切换到dev下的配置了

方法2:yml的多文档块

yml和properties配置文件中一样,但是使用yml去实现不需要创建多个配置文件,更加方便。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server:
port: 8081
#选择要激活那个环境块
spring:
profiles:
active: prod

---
server:
port: 8083
spring:
profiles: dev #配置环境的名称

---
server:
port: 8084
spring:
profiles: prod #配置环境的名称

Note:如果yml和properties同时都配置了端口,并且没有激活其他环境,默认会使用properties配置文件。

6.2 配置文件加载位置

外部加载配置文件的方式很多,我们选择最常用的即可,在开发的资源文件中进行配置。

官方外部配置文件说明参考文档:Spring Boot Reference Documentation

springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件。

1
2
3
4
优先级1:项目路径下的config文件夹配置文件
优先级2:项目路径下配置文件
优先级3:资源路径下的config文件夹配置文件
优先级4:资源路径下配置文件

优先级由高到底,高优先级的配置会覆盖低优先级的配置。

SpringBoot会从这四个位置全部加载主配置文件,互补配置。

在最低级的配置文件中设置一个项目访问路径的配置来测试互补问题

1
2
#配置项目的访问路径
server.servlet.context-path=/lin

扩展:指定位置加载配置文件

还可以通过spring.config.location来改变默认的配置文件位置

项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置,相同配置,外部指定的配置文件优先级最高。

1
2
java -jar spring-boot-config.jar --
spring.config.location=F:/application.properties

7. 自动装配原理

配置文件到底能写什么

SpringBoot官方文档:Spring Boot Reference Documentation

分析自动配置原理

以HttpEncodingAutoConfiguration(Http编码自动配置)为例解释自动配置原理:

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
//表示这是一个配置类,和以前编写的配置文件一样,也可以给容器中添加组件
@Configuration

//启动指定类的ConfigurationProperties功能
//进入这个HttpProperties查看,将配置文件中对应的值和HttpProperties绑定起来
//并把HttpProperties加入到IOC容器中
@EnableConfigurationProperties({HttpProperties.class})

//Spring底层@Conditional注解
//根据不同的条件判断,如果满足指定的条件,整个配置类里面的配置就会生效;
//这里的意思就是判断当前应用是否是web应用,如果是,当前配置类生效
@ConditionalOnWebApplication(
type = Type.SERVLET
)

//判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器
@ConditionalOnClass({CharacterEncodingFilter.class})

//判断配置文件中是否存在某个配置:spring.http.encoding.enabled;
//如果不存在,判断也是成立的
//即使我们配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的
@ConditionalOnProperty(
prefix = "spring.http.encoding",
value = {"enabled"},
matchIfMissing = true
)

public class HttpEncodingAutoConfiguration {
//他已经和SpringBoot的配置文件映射了
private final Encoding properties;
//只有一个有参构造器的情况下,参数的值就会从容器中拿
public HttpEncodingAutoConfiguration(HttpProperties properties) {
this.properties = properties.getEncoding();
}

//给容器中添加一个组件,这个组件的某些值需要从properties中获取
@Bean
@ConditionalOnMissingBean //判断容器没有这个组件?
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(org.springframew
ork.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(org.springframe work.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
return filter;
}
//...
}

总结:根据当前不同的条件判断,决定这个配置类是否生效

  • 一但这个配置类生效;这个配置类就会给容器中添加各种组件。
  • 这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的。
  • 所有在配置文件中能配置的属性都是在xxxxProperties类中封装着。
  • 配置文件能配置什么就可以参照某个功能对应的这个属性类。
1
2
3
4
5
//从配置文件中获取指定的值和bean的属性进行绑定
@ConfigurationProperties(prefix = "spring.http")
public class HttpProperties {
// .....
}

精髓

  1. SpringBoot启动会加载大量的自动配置类
  2. 看需要的功能有没有在SpringBoot默认写好的自动配置类当中
  3. 看这个自动配置类中到底配置了哪些组件(只要我们要用的组件存在在其中,我们就不需要再手动配置了)
  4. 给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中 指定这些属性的值即可。

xxxxAutoConfigurartion:自动配置类,给容器中添加组件

xxxxProperties:封装配置文件中相关属性


SpringBoot入门及原理
https://yiqiangshiyia.cn/2022/10/01/SpringBoot入门及原理/
作者
一腔诗意啊
发布于
2022年10月1日
许可协议