红茶的个人站点

  • 首页
  • 专栏
  • 开发工具
  • 其它
  • 隐私政策
Awalon
Talk is cheap,show me the code.
  1. 首页
  2. 其它
  3. 正文

Spring Boot 学习笔记1:基础

2023年9月3日 994点热度 0人点赞 0条评论

1.快速开始

用 Idea 可以很容易地创建 SpringBoot 应用:

image-20230902113353578

选择合适的 SpringBoot 版本以及需要的依赖:

image-20230902113539706

目前依然在维护的 2.X 版本是 2.7.15,不同的版本对 java 版本的需求不同,比如 3.X,要求最低 Java 17,2.7.15 要求最低 Java 8,关于 Spring Boot 对环境的要求可以查看对应的官方文档。

自动生成框架代码后,我们不需要额外配置,只需要添加一个 Controller 就可以完成最简单的一个 Web 应用示例:

@RestController
@RequestMapping("/books")
public class BookController {
    @GetMapping("/{id}")
    public Book getBookInfo(@PathVariable("id") Integer id){
        Book book = new Book();
        book.setId(id);
        book.setName("巴顿传");
        book.setType("人物传记");
        return book;
    }
}

当然还有对应的实体类,这里不做展示。

SpringBoot 内部包含一个 Tomcat,所以不需要我们添加 Tomcat Maven 插件,可以直接从入口类启动程序:

image-20230902170827275

当然,通过 Maven 生命周期启动(mvn run)也是可行的。

默认 SpringBoot 监听 8080 端口,所以这里访问 http://localhost:8080/books/2 就能看到接口返回信息。

相比之前已经学习过的 SSH 框架(Spring+SpringMVC+Mybatis),无疑 SpringBoot 更为方便,对于开发一个 Web 应用来说,这种方便体现在:

  • 不需要手动添加诸多依赖,比如 spring-mvc、servelet-api 等。

  • 不需要创建 Spring 和 SpringMVC 的配置类,以及相应的组件扫描和资源文件引用。

  • 不需要添加 Servlet 配置的相关类(向 Servlet 注入 Spring 容器)

  • 不需要从 Maven 模版生成框架代码,以及补齐缺失的目录。

  • 自身包含 Tomcat,无需额外准备 Tomcat 环境即可启动。

2.Spring Boot Maven 插件

默认生成的 Spring Boot 框架的 pom.xml 配置文件中包含一个插件:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
</plugin>

这个插件的用途是在对 Spring Boot 应用打包时,将其依赖的其它 Jar 包都打包进去,并指定 Spring Boot 的 jar 包作为入口程序。这样打包后的 Jar 包就可以在任何安装了 Java 环境的机器上以 Jar 包方式运行(java -jar xxx)。

  • 默认情况下(没有 Spring Boot Maven)插件,Maven 打包后的 Jar 包是不会包含其它依赖的 jar 包的。

  • war 包与 jar 包不同,默认情况下打包后的 war 包就会包含所依赖的其它 jar 包。

3.Starter

在之前介绍 Maven 的文章中,我们已经看到了一个 Maven 模块,可以通过继承的方式从父模块那里继承依赖,也可以通过依赖传递的方式从包含的依赖中获取更多的间接依赖。

实际上 Spring Boot 就是利用这一点来简化 Spring Boot 中的依赖添加。

示例项目的 pom.xml 包含一个父模块配置:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.15</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

我们可以点击查看父模块 spring-boot-starter-parent 的 pom.xml 内容。

3.1.spring-boot-starter-parent

其中定义了使用的 Java 版本与编码:

  <properties>
    <java.version>1.8</java.version>
    <resource.delimiter>@</resource.delimiter>
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  </properties>

开源许可证:

<license>
    <name>Apache License, Version 2.0</name>
    <url>https://www.apache.org/licenses/LICENSE-2.0</url>
</license>

资源过滤器:

<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>

这个资源过滤器的设置表示项目的资源目录(/resources)下的配置文件(properties 或 yaml)中可以使用 Maven 变量。

插件管理:

<pluginManagement>
    <plugins>
        <plugin>
            <!-- ... -->
        </plugin>
    </plugins>
</pluginManagement>

更多的信息保存在其父模块中:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.7.15</version>
</parent>

查看其父模块 spring-boot-dependencies 的 pom 文件。

3.2.spring-boot-dependencies

可以看到其包含非常多的自定义属性:

<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>
    <!-- ... -->
</properties>

这些属性用于定义依赖管理:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-amqp</artifactId>
            <version>${activemq.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-blueprint</artifactId>
            <version>${activemq.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-broker</artifactId>
            <version>${activemq.version}</version>
        </dependency>
        <!-- ... -->
    </dependencies>
</dependencyManagement>

也就是说,基本上我们在开发 Spring Boot 应用时会使用的依赖,都被 spring-boot-dependencies 管理了起来,我们就不用纠结使用何种版本的依赖以及会不会引发兼容性问题。

与依赖类似,我们可能用到的插件同样被管理起来了:

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <version>${build-helper-maven-plugin.version}</version>
            </plugin>
            <plugin>
                <groupId>org.flywaydb</groupId>
                <artifactId>flyway-maven-plugin</artifactId>
                <version>${flyway.version}</version>
            </plugin>
            <plugin>
                <groupId>pl.project13.maven</groupId>
                <artifactId>git-commit-id-plugin</artifactId>
                <version>${git-commit-id-plugin.version}</version>
            </plugin>
            <!-- ... -->
        </plugins>
    </pluginManagement>
</build>

我们同样不需要纠结版本的问题。

3.3.spring-boot-starter-test

在我们项目的 pom.xml 中,除了父模块,还默认添加了一个用于测试的依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

这个依赖中包含了测试所需的各种依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.3.29</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.8.2</version>
        <scope>compile</scope>
    </dependency>
    <!-- ... -->
</dependencies>

如果我们的项目需要添加单元测试,我们只需要添加这一个依赖即可,不需要手动添加多个依赖。

3.4.spring-boot-starter-web

与之类似,因为我们这里是一个 Web 应用,所以在创建 SpringBoot 项目时选择了 Web 功能,这体现在初始化的 pom.xml 中会自动添加一个 spring-boot-starter-web 依赖:

<dependencies>
    <!-- ... -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <version>2.7.15</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>5.3.29</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.29</version>
        <scope>compile</scope>
    </dependency>
</dependencies>
</project>

这其中的依赖 spring-boot-starter-tomcat 包含 SpringBoot 内嵌的 Tomcat 所需的依赖:

<dependencies>
    <dependency>
        <groupId>jakarta.annotation</groupId>
        <artifactId>jakarta.annotation-api</artifactId>
        <version>1.3.5</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-core</artifactId>
        <version>9.0.79</version>
        <scope>compile</scope>
    </dependency>
    <!-- ... -->
</dependencies>

SpringBoot 应用通过入口类启动时,会自动检查包含的依赖,如果有 spring-boot-starter-web 相关依赖,就会以 Web 应用的方式启动内置的 Tomcat 服务。

3.5.总结

也就是说,SpringBoot 通过用父模块对依赖和插件进行版本管理,并且按照功能,将我们可能用到的依赖封装成各种 starter 命名的依赖,我们需要何种功能的相关依赖,只要引用对应的 starter 即可。通过这种方式,SpringBoot 简化了项目开发时的依赖添加和管理。

3.6.案例:使用 Jetty

Jetty 是一个和 Tomcat 用途相同的 Web 服务器软件,它比 Tomcat 更轻量级。利用 Spring Boot 的 starter,我们可以很容易地将 Spring Boot 使用的 Web 服务器软件从 Tomcat 替换为 Jetty。

首先我们需要屏蔽掉 Tomcat 相关依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>

添加 Jetty 相关依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
  • 和 Tomcat 一样,所有依赖都包含在一个 starter 里,我们只要添加和删除就能增加或移除相应的功能。

  • 这个依赖项同样可以在 spring-boot-dependencies 的 pom 中找到。

启动服务后就能看到 Spring Boot 已经改为使用 Jetty:

main] org.eclipse.jetty.util.log
main] o.s.b.w.e.j.JettyServletWebServerFactory
main] org.eclipse.jetty.server.Server

4.配置文件

4.1.格式

SpringBoot 可以配置三种类型的配置文件:

  • properties

  • yml

  • yaml

下面以修改应用监听端口的配置说明它们的写法:

properties

server.port=80

yml

server:
  port: 81

注意,:后属性值之前有一个空格。

yaml

server:
  port: 82

yaml 与 yml 的格式完全相同。

4.1.1.优先级

一般推荐使用 yml 配置文件,但如果项目中同时存在三种形式的配置文件,其优先级为:properties > yml > yaml。

这点可以通过配置不同的监听端口,并观察哪个配置生效来验证。

4.1.2.自动提示

使用 Idea 编辑配置文件时,会有很方便的自动提示:

image-20230903105814288

能够自动提示的前提是 Idea 要能正确识别到哪些文件是配置文件,如果因为某些原因没有正确识别,也就不会有自动提示。这时候需要我们手动添加对应的文件为项目的配置文件。

具体的添加配置文件的功能在 Project Structure 的 Facets 标签页中:

image-20230903110312902

4.2.YAML格式

YAML(YAML Ain't Markup Language),一种数据序列化格式。这种格式的配置文件在近些年已经占有主导地位。

YAML格式的文件扩展名可以是yml也可以是yaml,一般使用yml。

4.2.1.语法

  • 大小写敏感

  • 属性层级关系使用多行描述,每行结尾使用冒号结束

  • 使用缩进表示层级关系,同层级左侧对齐,只允许使用空格(不允许使用Tab键)

    空格的个数并不重要,只要保证同层级的左侧对齐即可。

  • 属性值前面添加空格(属性名与属性值之间使用冒号+空格作为分隔)

  • # 表示注释

在 yaml 文件中如果要表示一个属性有多个值(数组):

my:
  hobbies:
    - music
    - draw
    - travel

5.读取配置

5.1.@Value

可以用之前介绍过的 @Value 注解注入的方式读取配置文件中的属性。

假设配置文件中的内容:

author:
  name: icexmoon
  age: 18
  hobbies:
    - music
    - draw
    - travel

用 @Value 读取:

@RestController
@RequestMapping("/config")
public class ConfigController {
    @Value("${author.name}")
    private String name;
    @Value("${author.age}")
    private Integer age;
    @Value("${author.hobbies[0]}")
    private String hobby1;

    @GetMapping("/print")
    public void print(){
        System.out.println(name);
        System.out.println(age);
        System.out.println(hobby1);
    }
}

5.2.Environment

SpringBoot 有一个预设的 Environment 类型的 Bean,可以用它来读取配置文件中的属性:

@RestController
@RequestMapping("/config")
public class ConfigController {
    @Autowired
    Environment environment;

    @GetMapping("/print")
    public void print(){
        System.out.println(environment.getProperty("author.name", String.class));
        System.out.println(environment.getProperty("author.age", Integer.class));
        System.out.println(environment.getProperty("author.hobbies[0]", String.class));
    }
}

5.3.@ConfigurationProperties

还可以使用@ConfigurationProperties注解定义的 Bean 读取配置:

@Component
@ConfigurationProperties(prefix = "author")
@Data
public class AuthorProperties {
    String name;
    Integer age;
    List<String> hobbies;
}

之后只要注解注入这个 Bean 并调用 Getter 方法就可以了:

@RestController
@RequestMapping("/config")
public class ConfigController {
    @Autowired
    private AuthorProperties authorProperties;

    @GetMapping("/print")
    public void print(){
        System.out.println(authorProperties.getName());
        System.out.println(authorProperties.getAge());
        System.out.println(authorProperties.getHobbies());
    }
}

创建自定义配置类后,Idea 会出现一个错误提示:

image-20230903170146840

提示我们需要创建一个注释处理器(Annotation Processor),你可以点击右上角链接跳转到官方文档复制相关依赖,并加入到你的项目中:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

这是 Spring 官方最推荐的方式,原因是:

  • 读取后的数据具有良好层次格式

  • 可以将数组形式的属性直接加载为一个数组或者序列(List)

  • 可以使用 Hibernate Validation 对属性值的合法性进行检验

关于最后一点,这里举一个简单的例子:

@Component
@ConfigurationProperties(prefix = "author")
@Data
@Validated
public class AuthorProperties {
    @NotNull
    @Length(min = 2,max = 10)
    String name;
    @NotNull
    @Min(0)
    @Max(150)
    Integer age;
    @NotNull
    @Size(min = 1, max = 10)
    List<String> hobbies;
}

这里使用了 Hibernate Validation 相关的注解(通过添加 spring-boot-starter-validation 依赖引入),这样 Spring 在将配置中的属性读入AuthorProperties这个类后,会按照相应注解的规则进行合法性校验。如果不通过,比如配置文件中设置的年龄大于150,应用启动时就会报错:

Property: author.age
Value: "200"
Origin: class path resource [application.yml] - 4:8
Reason: 最大不能超过150

这有助于严格约束配置文件中的属性值,并且尽早发现因为属性值设置错误导致的bug。

6.多环境开发

一般我们开发都需要将多个环境下的配置做区分,比如开发、测试、生产要连接不同的数据库。

6.1.yml

在 SpringBoot 中,很容易可以在 yml 配置中区分多个环境下的配置:

# 默认配置
author:
  name: icexmoon
  age: 18
  hobbies:
    - music
    - draw
    - travel

spring:
  profiles:
    active: dev
# 开发环境
---
spring:
  config:
    activate:
      on-profile: dev
server:
  port: 80

# 测试环境
---
spring:
  config:
    activate:
      on-profile: test
server:
  port: 81

# 生产环境
---
spring:
  config:
    activate:
      on-profile: prd
server:
  port: 82

这里用---可以将一个 yml 文件拆分成多个配置文件,每个配置文件的标识用spring.config.activate.on-profile属性定义。

配置文件标识也可以用 spring.profiles 属性定义,不过该属性已经被废弃。

最上方的部分为默认属性,启用哪个配置文件由其中的spring.profiles.active属性决定。

6.2.properties

如果使用 properties 配置,需要使用多个 properties 文件保存不同环境下的配置信息:

  • application.properties ,主配置文件

  • application-dev.properties,开发环境配置文件

  • application-test.properties,测试环境配置文件

  • application-prd.properties,生产环境配置文件

同样的,具体加载哪个环境的配置文件由主配置文件中的spring.profiles.active属性决定。

6.3.命令行启动

当 SpringBoot 应用打成 jar 包并通过命令行的方式启动时,可以添加命令行参数覆盖配置中的属性值。

比如,通常打成的 jar 包都是加载开发环境的配置文件,如果要加载其它环境,可以:

java -jar .\boot-demo-0.0.1-SNAPSHOT.jar --spring.profiles.active=test

可以覆盖多个配置属性:

java -jar .\boot-demo-0.0.1-SNAPSHOT.jar --spring.profiles.active=test --server.port=8080

实际上 SpringBoot 应用使用的属性来源是很多的,有默认属性、有来自配置文件、还有来自系统环境变量等等,这些来源是有优先级的,如果多个来源都定义了同一个名称的属性,优先级高的生效。

关于属性的优先级,可以查看官方文档。

6.4.Maven 属性

在前文介绍过如何在配置文件中使用 Maven 属性,我们可以利用这点来通过 Maven 控制那个环境的配置文件生效:

<profiles>
    <profile>
        <id>dev</id>
        <properties>
            <profiles.active>dev</profiles.active>
        </properties>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
    </profile>
    <profile>
        <id>test</id>
        <properties>
            <profiles.active>test</profiles.active>
        </properties>
    </profile>
    <profile>
        <id>prd</id>
        <properties>
            <profiles.active>prd</profiles.active>
        </properties>
    </profile>
</profiles>

这里定义了三个 Maven “配置文件”,分别命名为 dev、test、prd,并且都包含一个 Maven 自定义属性 profiles.active,这个属性的值对应 yml 文件中的不同环境的配置文件名称。

在 yml 文件中使用上边定义的 Maven 属性:

spring:
  profiles:
    active: ${profiles.active}

这样还不能让 yml 文件中的 Maven 属性生效。如果编译项目,就会在 jar 包中看到 yml 的实际内容是:

spring:
  profiles:
    active: ${profiles.active}

要让配置文件中的 Maven 属性生效,还需要在 POM 文件中添加一个插件:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-resources-plugin</artifactId>
    <configuration>
        <encoding>utf-8</encoding>
        <useDefaultDelimiters>true</useDefaultDelimiters>
    </configuration>
</plugin>

在这里与之前的做法有所不同,没有使用 Resource 标签,因为这里不是处理子模块配置中的 Maven 属性。

再次编译后就能看到 yml 文件的内容已经被 Maven 属性替换:

spring:
  profiles:
    active: dev

使用 Maven 属性控制生效的环境配置有一个好处:可以更灵活地为特定环境生成 jar 包。

比如,在当前示例中,我们生成的 jar 包如果不加命令行参数,默认使用开发环境的配置运行。我们可以在打包时添加 Maven 参数来生成一个默认使用测试环境配置运行的 jar 包:

mvn package --activate-profiles test

用同样的方式你可以生成一个默认使用生产环境配置运行的包。

这样做的好处是在运行是不需要再使用--spring.profiles.active=xxx来修改加载的环境配置。

6.5.分层配置

最常见的配置文件(比如 application.yml)是放在项目的 /resources 目录下的,其实除了那里,还可以放在多个地方。

假设默认的 /resources/application.yml 中的一个自定义属性设置如下:

# 默认配置
author:
  name: icexmoon
  age: 18
  hobbies:
    - music
    - draw
    - travel

我们打算将其值重新定义,但又不想直接修改这个 yml 文件(这么做的一个可能的理由是这个文件在代码版本库中)。那么我们就可以添加一个 /resources/config/application.yml 文件:

# 默认配置
author:
  name: jackchen
  age: 25
  hobbies:
    - music
    - draw
    - travel

现在运行程序并进行测试就能看到实际上是 /resources/config/application.yml 配置在生效:

jackchen
25
[music, draw, travel]

在项目被打成 jar 包后运行时,我们也可以用同目录下添加的配置文件来覆盖属性。

比如在生成的 jar 包所在的目录添加 application.yml:

# 默认配置
author:
  name: BrusLee
  age: 30
  hobbies:
    - music
    - draw
    - travel

运行程序并进行测试,输出:

BrusLee
30
[music, draw, travel]

利用这种特性,我们可以在部署 jar 包的机器上配置一些和环境相关的特殊配置信息。

比如为前端测试人员编写一个专用的配置文件,以让 jar 包可以运行在前端人员的电脑上。

与本地开发类似,jar 包所在的目录下同样可以添加一个 ./config/application.yml 配置文件,该配置文件的优先级是最高的,可以覆盖掉同目录下的配置文件。

总结一下,我们可以在四个地方存放配置文件:

  • 项目的 /resources 目录

  • 项目的 /resources/config 目录

  • jar 包所在的目录

  • jar 包所在目录的 ./config 目录

优先级是依次提高的。

我觉得这种“分层配置”功能最实用的一个应用是,我们可以在开发环境 jar 包所在的目录添加一个 yml:

spring:
  profiles:
    active: test

因为在工作中我遇到过几次因为打的 jar 包使用的默认配置不是环境对应的配置,或者在运行是没有添加 --spring.profiles.active=xxx 参数引发的莫名其妙的 bug。有了上面这个“本地配置文件”,就可以减少这种情况的出现概率。

The End,谢谢阅读。

本文的完整示例可以从这里获取。

7.参考资料

  • 黑马程序员SSM框架教程

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: spring spring boot yml
最后更新:2023年9月3日

魔芋红茶

加一点PHP,加一点Go,加一点Python......

点赞
< 上一篇
下一篇 >

文章评论

取消回复

*

code

COPYRIGHT © 2021 icexmoon.cn. ALL RIGHTS RESERVED.
本网站由提供CDN加速/云存储服务

Theme Kratos Made By Seaton Jiang

宁ICP备2021001508号

宁公网安备64040202000141号