SpringBoot 配置文件

目录

[1. 配置文件](#1. 配置文件)

[1.1 什么是配置文件](#1.1 什么是配置文件)

[1.2 配置文件存放的内容](#1.2 配置文件存放的内容)

[1.3 配置文件的作用](#1.3 配置文件的作用)

[2. SpringBoot 配置文件](#2. SpringBoot 配置文件)

[3. properties](#3. properties)

[3.1 读取配置文件](#3.1 读取配置文件)

[3.1.1 @Value](#3.1.1 @Value)

[3.1.2 @PostConstruct](#3.1.2 @PostConstruct)

[3.2 properties 缺点](#3.2 properties 缺点)

[4. yml](#4. yml)

[4.1 yml 基本语法](#4.1 yml 基本语法)

[4.2 yml 配置数据类型](#4.2 yml 配置数据类型)

[4.2.1 yml 读取配置](#4.2.1 yml 读取配置)

[4.3 yml 配置对象](#4.3 yml 配置对象)

[4.3.1 读取对象数据](#4.3.1 读取对象数据)

[4.2 yml 配置集合](#4.2 yml 配置集合)

[4.2.1 配置 List & Map](#4.2.1 配置 List & Map)

[5. 综合练习](#5. 综合练习)

[5.1 验证码案例](#5.1 验证码案例)

[5.1.1 需求](#5.1.1 需求)

[5.1.2 接口文档](#5.1.2 接口文档)

[5.1.2.1 生成验证码](#5.1.2.1 生成验证码)

[5.1.2.2 校验用户输入](#5.1.2.2 校验用户输入)

[5.1.3 接口一: 生成验证码](#5.1.3 接口一: 生成验证码)

[5.1.4 接口二: 校验用户输入](#5.1.4 接口二: 校验用户输入)

[5.1.5 后端代码](#5.1.5 后端代码)

[5.1.6 前端代码](#5.1.6 前端代码)


1. 配置文件

1.1 什么是配置文件

其实, 我们一直都在接触配置文件, 例如一个项目中的 .idea 文件夹中的文件, 就是一个配置文件:

这些配置文件中包含了 IDEA 的背景色, 字体大小, Maven 仓库的地址, ....... 每个项目都有自己的配置文件.

包括我们电脑中的 user 文件夹, 里面也有配置文件:

1.2 配置文件存放的内容

上文说到, 每个项目都有不同的设置, 因此具有不同的配置文件.

那么, 对于用户来说也是一样的, 为了满足每个用户可能存在的不同需求, 我们将可能发生变化的信息, 从代码中分离出来, 放置在配置文件中, 实现和代码的解耦, 以便在不修改代码的情况下, 根据用户的偏好或需求进行调整, 从而解决硬编码问题.

硬编码, 就是指代码写死的, 难以修改的.

1.3 配置文件的作用

因此, 配置文件的作用如下:

  • 通过修改配置文件, 可以根据用户需求对程序相关功能进行更改, 而无需修改代码和重新部署, 因此解决了硬编码问题.

2. SpringBoot 配置文件

SpringBoot 配置文件有以下三种(文件名必须为 application, 不可修改):

  1. application.properties
  2. application.yml
  3. application.yaml

properties、yml 和 yaml 不仅仅代表 SpringBoot 的配置文件, 更重要的是它们代表了不同的**文件格式,**每种格式都有自己的语法规则.

其中, yml 就是 yaml 的简写(就像 htm 是 html 的简写一样), 两者代表的是同一个东西.

3. properties

properties 的语法为 key-value 格式, key 和 value 之间使用 = 进行分割. 如:"key = value"

建议: 所有单词小写, 单词之间使用 点(.) 进行分割.

Spring 项目创建时, 就已经帮我们创建了 application.properties 格式的配置文件:

我们可以在配置文件中修改服务器的端口号:

3.1 读取配置文件

3.1.1 @Value

除了程序自带的配置信息外, 我们也可以添加自定义的配置信息:

我们可以通过 @Value 注解将配置信息赋值给属性, 进而读取这些配置信息:

Spring 通过 @Value 拿到配置信息后(初始为字符串类型), 会根据属性的类型, 对字符串进行类型转换.

如果可以转换, 则成功:

如果不可以转换, 则报错:

3.1.2 @PostConstruct

除了上述通过 http 请求来展示配置信息的方式外, 我们还可以通过 @PostConstruct 注解来展示获取到的信息.

@PostConstruct 是一个方法注解.

  1. 当前类的 Bean 完成实例化 (注意是实例化, 不是初始化)

  2. Bean 中所依赖的其他 Bean(属性 Bean) 完成完成注入(初始化)

  3. 执行 @PostConstruct

实例化: 申请内存空间, 赋予默认值(null / 0 / false 等)

初始化: 把当前类的 Bean 所依赖的其他 Bean(属性 Bean) 完成注入

因此, 在该方法执行时, 当前类 Bean 中的属性 Bean 已经被注入(完成了初始化), 因此不会出现空指针异常的情况.

3.2 properties 缺点

由于 properties 的语法, 使得 properties 类型的配置文件有一个明显的缺点: 配置信息冗余.

例如, 数据库相关的配置文件:

此时, 就需要 yml 出场了.

4. yml

  1. application.yml 可以和 application.properties 并存在 SpringBoot 项目中, 并且两个配置文件同时生效(取并集).
  2. 若两个配置文件存在冲突的部分(如: 在两个文件中都修改了端口号), 那么会以 .properties 为准(properties 的优先级更高).

4.1 yml 基本语法

yml 采用树形结构, 基本语法如下图所示:

接下来展示一下 properties 和 yml 的对比:

尤其需要注意的是:

  1. 第一级不能有空格
  2. 第一级的 key 名不能重复出现
  3. 同一级一定要对齐

yml 中其实也可以使用 点(.) 来进行单词间的分割, 就像 properties 形式一样, 但是 key 和 value 一定要使用 冒号(:) 来分割:

4.2 yml 配置数据类型

# 字符串
string:
  value: Hello
# 布尔值,true或false
boolean: 
  value: true
  value1: false
# 整数
int:
  value: 10
# 浮点数
float: 
  value: 3.14159
# Null,~代表null
# null 是特殊字符, 需要使用 "" 引起来
"null": 
  value: ~
# 空字符串(以下几种均表示 空字符串)
empty: 
  value: ""
  value1: ''
  value2: 

需要额外注意的是 null 和 空字符串.

4.2.1 yml 读取配置

读取 yml 配置文件中的信息, 方式和 properties 是一模一样的.

同样是使用 @Value 将配置信息注入到属性中, 并且单词间使用 点(.) 进行分割:

4.3 yml 配置对象

yml 定义对象, 和我们上面定义数据时的语法格式是相同的:

上图表示的就是, 一个 person 类中, 定义了 id, name, age 三个属性.

4.3.1 读取对象数据

@ConfigurationProperties 是一个类注解和方法注解.

对类使用, 并指定其 prefix 属性, 表示将配置中前缀为 prefix 的信息, 读取到该 Java 类的属性中.

注意:

使用 @ConfigurationProperties 将 JavaBean 中的属性和 配置文件中对象的属性进行匹配时, 两者名字要以一一对应!!

Bean 的生命周期如下:

同样, 也可以使用 @Value 读取配置中对象的某一属性信息:

因此, @Value 和 @ConfigurationProperties 的关系如下:

  1. 本质都是将配置数据读取到属性的 Bean 中,用于实现配置的注入,且都发生在 Bean 初始化过程中
  2. @Value: 用于注入单个属性的值. 通常用于注入简单类型的属性, 例如字符串、数字、布尔值等.
  3. @ConfigurationProperties: 用于将一个配置对象的信息, 注入到整个 Java 类的属性中.

4.2 yml 配置集合

4.2.1 配置 List & Map

在配置文件中配置 List(或数组), Map, 也是以对象的形式来配置的:

读取 List 和 Map 的配置信息, 同样是使用 @ConfigurationProperties:


5. 综合练习

5.1 验证码案例

5.1.1 需求

  1. 页面生成验证码
  2. 输入验证码, 点击提交, 验证用户输入验证码是否正确, 正确则进行页面跳转

5.1.2 接口文档

后端需要提供两个接口:

  1. 生成验证码图片
  2. 校验用户输入的验证码是否正确
5.1.2.1 生成验证码

请求: (前端给后端服务器发送一个请求)

  • URL: /captcha/getCaptcha

响应: 后端生成一个验证码的图片, 显示在页面上

5.1.2.2 校验用户输入

请求 URL: /captcha/check

请求参数: 用户输入的值

响应: 根据用户输入的验证码, 校验验证码是否正确. true: 验证成功. false: 验证失败

5.1.3 接口一: 生成验证码

我们借助开源的 hutool 来生成验证码: Hutool参考文档

当然, 要想使用 hutool, 第一步是先引入 hutool 的 Maven 依赖:

引入 hutool 依赖后, 我们就能够导入 hutool 提供的包, 使用 hutool 所提供的接口了:

了解了 hutool 后, 接下来正式开始接口的定义.

因为我们后端生成的验证码大小, 是可变的, 因此可以将验证码图片的高, 宽, ... 定义在配置文件中:


(注意: 内部类 Session 必须是静态的!!)

并且, 生成验证码后, 我们需要存储两个值:

  1. 将验证码的正确值, 存储在用户的 Session 中, 以便后续在验证接口中获取并验证用户输入的值是否正确
  2. 将用户开始填写时(生成验证码完毕时)的时间戳存储到用户 session 中, 以便后续在验证接口中获取并验证用户输入所用时间是否超时

演示:

在这个接口中, 我们需要注意两点:

1. 手动将响应的 Content-Type 设置为 "image/jpeg"

首先, Spring 确实会根据返回的内容自动推测 Content-Type. 如果你返回的是一张图片, Spring 会根据图片的格式(例如 .jpg, .png, .gif 等)自动设置合适的 Content-Type.此外, 浏览器也会有一些智能机制, 自动通过内容来推测 Content-Type.

但是, 为了避免不必要的兼容性问题或误判, 最好还是手动设置 Content-Type, 明确告诉浏览器类型, 确保浏览器按照该类型进行解析.

java 复制代码
// 手动将响应的 Content-Type 设置为 图片格式, 以便浏览器进行解析
response.setContentType("image/jpeg");

2. 将 get 请求设置为无缓存形式

当我们通过浏览器的地址栏输入 URL 访问一个页面时, 此时的请求方式是 GET 请求.

而浏览器和服务器通常会缓存 GET 请求的响应, 目的是提高页面加载速度并减少不必要的网络请求.

因此, 当用户连续刷新页面时, 返回的都可能是同一个验证码.

取消 get 请求缓存的方式有两种:

  1. 通过前端取消缓存(通常)
  2. 通过后端取消缓存

在实际工作中, 通常由前端解决这个问题.

前端解决方式:

html 复制代码
  <!-- 将 get 请求设置为无缓存形式(放在 head 标签中) -->
  <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
  <meta http-equiv="Pragma" content="no-cache" />
  <meta http-equiv="Expires" content="0" />

后端的解决方式:

java 复制代码
// 设置无缓存形式(通常是由前端处理的)
response.setHeader("Pragma", "no-Cache");

3. 将 Spring 所管理类的内部类设为静态

我们上文说到, Session 必须作为一个静态内部类存在, 接下来解释原因.

如果内部类不是静态的, 那么外部类 Bean 和内部类 Bean 就会形成一个 "死循环", 导致 Spring 无法管理. 原因:

  1. 要创建外部类的 Bean, Spring 需要先实例化外部类
  2. 但是,要实例化外部类, Spring 必须先处理其内部类的 Bean(因为可能在外部类中使用了内部类的实例作为字段)
  3. 由于内部类是非静态的, 所以要实例化内部类的 Bean, Spring 必须先有一个外部类的实例
  4. 这就形成了一个死循环: 外部类依赖于内部类, 内部类又依赖于外部类, 导致 Spring 无法完成 Bean 的创建

**而静态内部类具有独立性,**静态内部类 (static 修饰) 不依赖于外部类的实例, 它可以像普通类一样独立地创建.

因此, Spring 可以直接实例化静态内部类, 而不会引发循环依赖问题.

结论: 如果一个类交给 Spring 来管理(即作为 Spring Bean), 其中的 内部类 必须是 静态的!!

5.1.4 接口二: 校验用户输入

演示:

5.1.5 后端代码

java 复制代码
@Data
@Configuration
@ConfigurationProperties(prefix = "captcha")
public class CaptchaProperties {
    private int width;
    private int height;
    private Session session;
    @Data
    public static class Session {
        private String key;
        private String date;
    }
}
java 复制代码
@RestController
@RequestMapping("/captcha")
public class CaptchaController {
    @Autowired
    private CaptchaProperties captchaProperties;
    // 定义超时时间
    private static final long TIME = 30 * 60 * 1000;
    @RequestMapping("/getCaptcha")
    public void getCaptcha(HttpServletResponse response, HttpSession session) throws IOException {
        // 手动将响应的 Content-Type 设置为 图片格式, 以便浏览器进行解析
        response.setContentType("image/jpeg");
        // response.setCharacterEncoding("utf-8");
        // 设置无缓存形式(通常是由前端处理的)
//        response.setHeader("Pragma", "no-Cache");
        // 定义图形验证码的长、宽、验证码字符数、干扰线宽度
        ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(captchaProperties.getWidth(), captchaProperties.getHeight());
        // 将验证码的值存到用户 Session 中
        session.setAttribute(captchaProperties.getSession().getKey(), captcha.getCode());
        // 存储验证码生成完毕(用户开始填写)时的时间戳
        session.setAttribute(captchaProperties.getSession().getDate(), new Date());
        // 直接把验证码写到浏览器
        captcha.write(response.getOutputStream());
    }

    /**
     * 验证输入的验证码是否正确
     * @param captcha 用户输入的验证码
     * @param session
     * @return true / false
     */
    @RequestMapping("/check")
    public boolean check(String captcha, HttpSession session) {
        // 从用户 session 中获取验证码的正确值
        String code = (String) session.getAttribute(captchaProperties.getSession().getKey());
        // 获取用户开始填写时的时间戳
        Date date = (Date) session.getAttribute(captchaProperties.getSession().getDate());
        // 验证输入值是否合法
        if (!StringUtils.hasLength(code)) {
            return false;
        }
        // 忽略大小写, 若输入正确 && 没有超时输入 && session 没有过期(data != null) => 验证通过
        if(code.equalsIgnoreCase(captcha) && date != null &&
                System.currentTimeMillis() - date.getTime() < TIME) {
            return true;
        }
        // 验证失败
        return false;
    }
}
java 复制代码
spring:
  application:
    name: spring-captcha-demo

captcha:
  width: 150
  height: 50
  session :
    key: CAPTCHA_SESSION_KEY
    date: CAPTCHA_SESSION_DATE

5.1.6 前端代码

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <!-- 将 get 请求设置为无缓存形式 -->
  <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
  <meta http-equiv="Pragma" content="no-cache" />
  <meta http-equiv="Expires" content="0" />

  <title>验证码</title>
  <style>
    #inputCaptcha {
      height: 30px;
      vertical-align: middle; 
    }
    #verificationCodeImg{
      vertical-align: middle; 
    }
    #checkCaptcha{
      height: 40px;
      width: 100px;
    }
  </style>
</head>

<body>
  <h1>输入验证码</h1>
  <div id="confirm">
    <input type="text" name="inputCaptcha" id="inputCaptcha">
    <img id="verificationCodeImg" src="/captcha/getCaptcha" style="cursor: pointer;" title="看不清?换一张" />
    <input type="button" value="提交" id="checkCaptcha">
  </div>
  <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
  <script>
    
    $("#verificationCodeImg").click(function(){
      $(this).hide().attr('src', '/captcha/getCaptcha?dt=' + new Date().getTime()).fadeIn();
    });

    $("#checkCaptcha").click(function () {
        $.ajax({
          type: "post",
          url: "/captcha/check",
          data: {
            captcha: $("#inputCaptcha").val()
          },
          success: function(res) {
            if(res) {
              location.href = "success.html";
            }else {
              alert("验证码错误!!");
            }
          }

        });
    });

  </script>
</body>

</html>
html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>验证成功页</title>
</head>
<body>
    <h1>验证成功</h1>
</body>
</html>

END

相关推荐
计算机毕设孵化场几秒前
计算机毕设-基于springboot的人工智能领域复合型人才校企协同培养管理系统的设计与实现(附源码+lw+ppt+开题报告)
java·spring boot·课程设计·计算机毕设论文·计算机毕业设计如何选题·计算机毕业设计选题推荐
青衫一笔墨1 小时前
企业级大模型应用的Java-Python异构融合架构实践
java·人工智能·python·架构
m0_748238921 小时前
Java进阶:Docker
java·docker·eureka
励碼2 小时前
BUG: 解决新版本SpringBoot3.4.3在创建项目时勾选lombok但无法使用的问题
java·spring boot·bug·lombok
Vacant Seat2 小时前
矩阵-旋转图像
java·数据结构·算法·矩阵
柃歌2 小时前
【UCB CS 61B SP24】Lecture 14 - Data Structures 1: Disjoint Sets学习笔记
java·数据结构·笔记·学习·算法
Yang-Never2 小时前
OpenGL ES -> GLSurfaceView绘制点、线、三角形、正方形、圆(索引法绘制)
android·java·开发语言·kotlin·android studio
扫地僧0092 小时前
Java 面试题及答案整理,最新面试题
java·jvm·算法·面试
float_六七2 小时前
Java中JDK、JRE,JVM之间的关系
java
茶本无香3 小时前
Spring Boot 与 MyBatis 数据库操作
数据库·spring boot·mybatis