小谢同学的小破站头像
【JavaEE进阶】SpringBoot 配置文件详解:properties 和 yml 的区别、优先级、读取方式封面图

【JavaEE进阶】SpringBoot 配置文件详解:properties 和 yml 的区别、优先级、读取方式

在这里插入图片描述

[JavaEE进阶] SpringBoot 配置文件详解


前言:
  这里是小谢同学的 SpringBoot 配置文件学习记录,整理自 JavaEE 进阶相关内容。有错误欢迎大家指出,专栏还有 Java、网络、C 语言等系列笔记欢迎翻阅。

  如果整篇只记一句话,我希望是这句:配置文件的本质,就是“把会变的东西从代码里挪出来”。而 SpringBoot 给了你两套写法——老派的 properties 和新潮的 yml,选哪个、怎么写、怎么读,这篇一次说清。

1. 配置文件是干嘛的?

 先说个大白话结论:配置文件就是“把写死在代码里的东西拿出来,放到一个集中的地方”。
 先说说什么叫“写死”(硬编码)。硬编码就是把数据直接嵌到程序源代码里,说白了就是“代码写死”。

 也就是不能根据用户的需求来进行调整,就好像手机的字体大小一样,每个人的喜好都各不一样,如果采取了硬编码的形式来编写代码,只有我们程序员来设置字体大小,写死了,有些用户视力,以及个人习惯都不一样,这样用户的体验感就非常不好~
 如果采用配置文件:把字号放在配置文件里,程序启动时读一下用户设置,按用户的偏好显示。这样能有效加强用户的体验感~

 同样的道理,配置文件能让程序完成用户与应用程序的交互,也能完成应用程序与其他程序的交互。
 那 SpringBoot 里,什么东西需要放到配置文件里?其实也就这几类:

配置内容为什么需要它
项目的启动端口SpringBoot 内置 Tomcat,默认 8080,但 8080 很可能被别的程序占了
数据库连接信息(url / 用户名 / 密码)每个项目连的数据库都不一样,持久层框架必须支持自定义
第三方系统的调用密钥不同环境、不同客户,密钥肯定不同
普通日志和异常日志用来发现和定位问题

 顺便说一下 SpringBoot 在这件事上的另一层贡献:它定义并统一了配置文件的格式,其他框架想集成进 SpringBoot,必须得按它的规矩来。这就是“规范”带来的好处。




2. 快速入手:把 8080 改成 9090

 前面说过,Tomcat默认的端口号是8080,例如,我们启动一个springboot项目:
在这里插入图片描述
 如果当端口号被占用时,这时候程序启动就会失败,此时就需要修改启动时的端口号
 spring项目在创建之初已经顺手帮我们建好了配置文件。打开 application.properties,改一行:

server.port=9090

 重启程序观察日志:

在这里插入图片描述

 端口号一定要选对。8080 常被 IDEA、其他 Java 进程、甚至微信开发者工具占着。启动报 Port 8080 was already in use 的时候,这时候不要急着怀疑代码,先去配置文件换个端口,或者改回来之前那个。




3. SpringBoot 支持的三种配置文件格式

 SpringBoot 的配置文件一共有三种:

  • application.properties
  • application.yml
  • application.yaml

 yml 是 yaml 的简写,实际开发中出现频率最高。yaml 和 yml 的用法完全一样,所以这里只来讲解 yml。
 程序启动时,SpringBoot 会自动从 classpath 路径找到并加载这几个文件(application.properties、application.yaml 或 application.yml)。当然,也可以自己指定,比如通过 spring.config.name 参数来改名字和路径。

 这就像商品的包装:

properties:是老款的包装
yml: 则是新款的包装

 spring默认创建时生成的是老款包装:properties

 这从而衍生出了一个面试题:yml和properties有什么区别?

说明内容
① 能共存吗?理论上 .properties 和 .yml 可以同时存在一个项目中,两个配置都会加载;如果内容冲突,以 .properties 为主,也就是 .properties 优先级更高
② 实际怎么用?虽然能共存,但真实业务里我们通常统一只用一种格式,这样更好维护,也能降低故障率

💡 一句话记住优先级:properties > yml > yaml(同名冲突时)。




4. properties 配置文件说明

properties 是最早期的配置文件格式,也是创建 SpringBoot 项目时的默认格式。

4.1 基本语法

properties 是以键值对的形式配置的,key 和 value 之间用 = 连接:

 例如:

# 配置项目端口号
server.port=8080
# 配置数据库连接信息
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/testdb?characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=root

💡 小技巧:配置文件中用 # 来添加注释信息。




4.2 读取配置:@Value

 主动读取配置文件中的内容,可以用 @Value 注解。@Value 使用 ${} 的格式读取:
 我们首先得在: application.properties 里先写一个自定义配置:

mykey.key1=helloWord

 然后在 Controller 里读它:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class PropertiesController {

    @Value("${mykey.key1}")
    private String key1;

    @RequestMapping("/key")
    public String key() {
        return "读取到值:" + key1;
    }
}

 浏览器访问 /key,就能看到 读取到值:helloWord。
在这里插入图片描述

💡 ${} 里写的是配置文件的 key 全名,别写成 Java 字段名。字段叫 key1,但配置里是 mykey.key1,写错了启动就报 Could not resolve placeholder。

在这里插入图片描述




4.3 properties 的缺点

 properties 是 key-value 的形式,好处是简单,坏处是——冗余信息特别多。
 当我们配置文件中写的特别多的时候,看起来就很冗余,让人看起来就不是特别的简洁明了,感觉看起来一坨一坨的,不方便程序员理解;
 我们来看上面那段数据库配置,spring.datasource 被重复写了三遍:

spring.datasource.url=...
spring.datasource.username=...
spring.datasource.password=...

 配置项一多,这种重复就爆炸式增长,我们想要去修改个前缀还得全局替换一遍。
 此时yml就能很好的解决冗余的问题,yml天生就是树形结构有很强的可读性




5. yml 配置文件说明

yml 是 YAML 的缩写,全称 Yet Another Markup Language,翻译过来就是“另一种标记语言”。名字很谦虚,格式很好用。

5.1 基本语法

 yml 是树形结构的配置文件,基础语法是 key: value。

 ❤️ 注意:key 和 value 之间使用英文冒号加空格的方式组成,冒号后面的空格是一定不可以省略!

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/dbname?characterEncoding=utf8&useSSL=false
    username: root
    password: root

 第一项(spring: 带空格)是正确写法,key 在 IDEA 里会高亮显示;如果写成 spring:datasource 少了空格,key 就不会高亮,配置也不会生效。

 两者的对比:
在这里插入图片描述

对比项propertiesyml
写法spring.datasource.url=... 平铺spring: 换行缩进,树形
层级表达靠 . 点号连接靠换行 + 缩进
前缀重复每行都要重复写 spring.datasource只写一次,子项缩进即可
可读性一般更好,一眼看出层级关系

❤️ 注意: yml 里 key: 后面必须跟一个空格。server:port 不会报错弹窗,但配置就是不生效,排查半天你才会发现少了个空格。-> 一个空格引发的惨案。

在这里插入图片描述

我们需要添加一个空格才能是yml文件进行生效:

在这里插入图片描述




5.2 使用进阶

 yml 支持的数据类型比 properties 丰富,字符串、布尔、整数、浮点、null、空串都能写:

5.2.1 配置不同数据类型及 null

例如:

# 字符串
string.value: hello
# 布尔值,true 或 false
boolean.value: true
boolean.value1: false
# 整数
int.value: 10
# 浮点数
float.value: 3.14159
# Null,~ 代表 null
null.value: ~
# "" 空字符串
# 直接后面什么都不加也可以,但这种写法不直观,更推荐用引号括起来
empty.value: ''
类型写法备注
字符串string.value: Hello默认不用加引号
布尔值true / false只能小写
整数int.value: 10—
浮点数float.value: 3.14159—
nullnull.value: ~~ 就是 null
空字符串empty.value: ''推荐用引号,直观看得出来

 读取方式和 properties 一模一样,还是 @Value:

@RestController
public class ReadYml {
    @Value("${string.value}")
    private String hello;

    @RequestMapping("/ymlKey")
    public String key() {
        return "读取到值:" + hello;
    }
}

访问 http://127.0.0.1:8080/ymlKey,输出 读取到值:hello。

在这里插入图片描述




5.2.2 单引号与双引号

字符串默认不用加单引号或双引号,但如果加了英文的单双引号,就表示特殊的含义。我们在 application.yml 里配置三段:

string:
  str1: Hello \n Spring Boot.
  str2: 'Hello \n Spring Boot.'
  str3: "Hello \n Spring Boot."

 读取代码:

@RestController
public class ReadYml {
    @Value("${string.str1}")
    private String str1;
    @Value("${string.str2}")
    private String str2;
    @Value("${string.str3}")
    private String str3;

    @RequestMapping("/yml")
    public String readYml() {
        System.out.println(str1);
        System.out.println(str2);
        System.out.println(str3);
        return "yml";
    }
}

 我们能观察到一下结果:
在这里插入图片描述
 详细:

Hello \n Spring Boot.      ← str1:不加引号,\n 就是个普通字符
Hello \n Spring Boot.      ← str2:单引号,\n 被转义,还是普通字符
Hello                       ← str3:双引号,\n 保持本意,真的换行
 Spring Boot.

 结论整理成一句话:单引号会把 \n 转义掉(变成普通字符串,不换行),双引号不转义(\n 表示它本身的含义,也就是换行)。

写法\n 的表现结果
不加引号按普通字符处理原样输出 Hello \n Spring Boot.
单引号 '...'转义,失去特殊功能原样输出 Hello \n Spring Boot.
双引号 "..."不转义,保留本意真的换行

这里的“转义”确实拗口,但记住一句话就行:单引号是“保护套”,把特殊字符保护起来当普通人;双引号是“放大器”,让特殊字符继续发挥它的特殊能力。




5.2.3 配置对象

 yml 里还可以直接配置一个对象,接下来我们来看一看如何来配置对象:
 我们首先定义好对象的属性:

@Data
public class Student {
    private int id;
    private String name;
    private int age;
}

@Data注解,在之前的文章中我们谈到过,这是一个工具类,方便我们程序员的开发,我们不需要自己写get和set方法等

student:
  id: 1
  name: Java
  age: 18

 或者用行内写法(和上面作用一致):

student: {id: 1,name: Java,age: 18}

 注意:这个时候我们就不能再去使用 @Value 去读了(一个个字段读太麻烦),要用@ConfigurationProperties:而不是使用@Value注解

@ConfigurationProperties(prefix = "student")
@Component
@Data
public class Student {
    private int id;
    private String name;
    private int age;
}

 调用类:

@RestController
public class StudentController {
    @Autowired
    private Student student;

    @RequestMapping("/readStudent")
    public String readStudent() {
        return student.toString();
    }
}

 访问 http://127.0.0.1:8080/readStudent,就能看到 Student(id=1, name=Java, age=18)。
在这里插入图片描述

  1. @ConfigurationProperties 只是“说明怎么绑定”,这个类必须被 Spring 管理才会生效,所以要加 @Component(或者在启动类上加 @EnableConfigurationProperties)。
  2. prefix 的值要跟配置文件的第一层 key 完全一致,而且建议全小写。写成 prefix = "Student" 或者 prefix = "student.id" 都会绑定失败,字段全是默认值。




5.2.4 配置集合 List

 配置文件里当然也能配 List:
 例如:

dbtypes:
  name:
    - hello
    - word
    - hi

 集合的读取和对象一样,还是使用 @ConfigurationProperties注解:

@Component
@ConfigurationProperties("dbtypes")
@Data
public class ListConfig {
    private List<String> name;
}

 访问的方式:

@RestController
public class ReadYml2 {

    @Autowired
    private ListConfig listConfig;

    @RequestMapping("/readList")
    public String readList() {
        return listConfig.toString();
    }
}

 访问 http://127.0.0.1:8080/readList
在这里插入图片描述




5.2.5 配置 Map

 同理,配置文件中Map 也能够配置:
 例如:

maptypes:
  map:
    k1: hello
    k2: word
    k3: hi

map在配置文件中是使用键值对形似的,前面是key,后面为value

 或者行内写法(作用一致):

maptypes: {map: {k1: kk1,k2: kk2, k3: kk3}}

  Map 的读取还是同样用 @ConfigurationProperties 注解:

@Data
@ConfigurationProperties(prefix = "maptypes")
@Component
public class MapConfig {
    private HashMap<String, String> map;
}

 浏览器获取结果:

@Data
@ConfigurationProperties(prefix = "maptypes")
@Component
public class MapConfig {
    private HashMap<String, String> map;
}

 访问 http://127.0.0.1:8080/readMap
在这里插入图片描述

小结:@Value 只能读“单个值”,一旦遇到**对象 / List / Map 这种复杂结需要使用@ConfigurationProperties。




5.3 yml 的优缺点

 我们先来看看yml文件的优点:

  1. 可读性高,写法简单,易于理解;
  2. 支持更多的数据类型,可以简洁地表达对象、数组、List、Map 等数据形态;
  3. 支持更多的编程语言,不止 Java,Golang、Python、Ruby、JavaScript 里也能用。

 缺点:

  1. 不适合写复杂的配置文件。

例如我们先来看看这段配置文件:

keycloak.realm = demo
keycloak.resource = fm-cache-cloud
keycloak.credentials.secret = d4589683-Oce7-4982-bcd3
keycloak.security[0].authRoles[0]= user
keycloak.security[0].collections[0].name = ssologinurl
keycloak.security[0].collections[0].patterns[0] = /login/*

 转成 yml 就变成这样:

keycloak:
  realm: demo
  resource: fm-cache-cloud
  credentials:
    secret: d4589683-Oce7-4982-bcd3
  security:
    - authRoles:
        - user
      collections:
        - name: ssologinurl
          patterns:
            - /login/*

 转换过程本身就挺费精力,如果我们的配置再复杂一点,可读性会变得更差,代码也更难写。

  1. 对格式有很强的要求,一个空格可能会引起一场血案。

排查半天发现少写了一个空格~




6. 综合练习:Hutool 图形验证码

 学完语法,来一个真实场景来练手——图形验证码。随着安全要求越来越高,现在项目里基本都有验证码,而且越来越复杂(图形验证码、行为验证码都成了趋势)。
 图形验证码,手机验证码,还有能滑动的验证码等等各种各样的验证码
 验证码可以使用前端来实现,也可以使用后端实现,网上插件也很多
 文章中使用:Hutool。来实现图形验证码,并且使用后端来进行实现:
 需求:

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

 准备工作: 创建项目 → 引入 Spring MVC 依赖 → 把前端页面放进项目的 static 目录。
 前后端交互接口约定:

接口请求方式URL参数响应
生成验证码GET/captcha/getCaptcha无验证码图片内容
校验验证码POST/captcha/checkcaptcha(用户输入)true 成功 / false 失败

 也就是说:浏览器发一个 /captcha/getCaptcha 请求,服务器返回一张图片,浏览器显示在页面上。
 我们先来了解了解Hutool :在这里插入图片描述
 Hutool 是什么? 它是一个 Java 工具包类库,对文件、流、加密解密、转码、正则、线程、XML 等 JDK 方法做了封装,组成各种 Util 工具类。小而全,用静态方法封装,能降低 API 学习成本,让 Java 也能“甜甜的”,也就是如何生成一些东西不需要我们程序员来进行编写,直接使用别人早已写好的代码。

 首先第一步我们得先引入他给我们的依赖:

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.48</version>
        </dependency>

 第二步:先把配置项挪到配置文件里
 注意,这才是文章的重点!把会变的东西(宽、高、Session 的 key 名)抽离出来,不写在代码里:

captcha:
  width: 100
  height: 40
  session:
    key: CAPTCHA_SESSION_KEY
    date: KAPTCHA_SESSION_DATE

 第三步:把配置项映射成 Java 对象

@Data
@Component
@ConfigurationProperties(prefix = "captcha")
public class CaptchaProperties {
    private Integer width;
    private Integer height;

    private Session session;

    @Data
    public static class Session {
        private String key;
        private String date;
    }
}

💡 这里演示了 yml 里的嵌套结构:captcha.session.key 对应 Java 里的静态内部类 Session 的 key 字段,层级一一对应。

 第四步:生成验证码的接口

@RequestMapping("/captcha")
@RestController
public class HuToolCaptchaController {

    @Autowired
    private CaptchaProperties captchaProperties;

    @RequestMapping("/getCaptcha")
    public void getCode(HttpSession session, HttpServletResponse response) throws IOException {
        // 定义图形验证码的长和宽(从配置文件读取)
        LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(
                captchaProperties.getWidth(),
                captchaProperties.getHeight());

        response.setContentType("image/jpeg");
        // 禁止使用缓存
        response.setHeader("Pragma", "No-cache");

        // 输出到页面
        lineCaptcha.write(response.getOutputStream());

        // 存储在 Session 中,校验时使用
        session.setAttribute(captchaProperties.getSession().getKey(), lineCaptcha.getCode());
        session.setAttribute(captchaProperties.getSession().getDate(), new Date());

        // 打印日志
        System.out.println("生成的验证码:" + lineCaptcha.getCode());

        // 关闭流
        response.getOutputStream().close();
    }
}

 启动项目,访问 http://127.0.0.1:8080/captcha/getCaptcha,就能看到验证码图片了。
在这里插入图片描述

💡 如果你的项目是 SpringBoot 2.x,把 jakarta.servlet.* 换成 javax.servlet.* 即可;3.x 及以上用 jakarta。

 第五步:校验验证码

```java
import org.springframework.util.StringUtils;

private final static long VALID_MILLIS_TIME = 60 * 1000;

@RequestMapping("/check")
public boolean checkHomeCaptcha(String captcha, HttpSession session) {
    if (!StringUtils.hasLength(captcha)) {
        return false;
    }
    String savedCaptcha = (String) session.getAttribute(captchaProperties.getSession().getKey());
    Date sessionDate = (Date) session.getAttribute(captchaProperties.getSession().getDate());

    if (captcha.equalsIgnoreCase(savedCaptcha)) {
        // 校验时间:一分钟以内有效
        if (sessionDate == null
                || System.currentTimeMillis() - sessionDate.getTime() < VALID_MILLIS_TIME) {
            return true;
        }
        return false;
    }
    return false;
}

逻辑就是:比对 Session 中存储的验证码和用户输入的是否一致;如果一致,并且时间在一分钟以内,就认为验证成功。注意 equalsIgnoreCase ——验证码是不区分大小写的。

 第六步:前端 ajax

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">

  <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 () {
        alert("验证码校验");
    });

  </script>
</body>

</html>

 大概就是这样的一个外形;
在这里插入图片描述
 我们需要修改这一部分代码:

    $("#checkCaptcha").click(function () {
        alert("验证码校验");
    });

修改后的代码:

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

 第七步:运行测试
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

 访问 http://127.0.0.1:8080/index.html,输入验证码,验证成功即可跳转。

💡 回头看这个案例:宽、高、Session 的 key 名全部来自配置文件,以后想让验证码变宽一点,改 yml 就行,一行 Java 代码都不用动。这就是配置文件最朴素也最核心的价值。




7. 总结与速查清单

核心三句话:

  1. 配置文件 = 把会变的信息从代码里挪出来,解决硬编码问题;
  2. properties 是 key=value 的键值形式,yml 是树形结构,靠缩进表达层级;
  3. 单个值用 @Value,对象 / List / Map 用 @ConfigurationProperties。

properties vs yml 速查

对比项propertiesyml
语法key=valuekey: value(冒号后必须有空格)
结构平铺,靠 . 连接树形,靠换行缩进
注释##
冗余多(前缀重复写)少(同层只写一次)
数据类型基本类型字符串/布尔/数字/null/对象/List/Map
适用场景简单配置、老项目日常开发首选
复杂度容忍度高(复杂配置更清楚)低(复杂配置可读性差)

Value 与ConfigurationProperties应该选哪一个?

注解写法能读什么备注
@Value@Value("${key}")单个值(String、int…)读不了对象 / List / Map
@ConfigurationProperties@ConfigurationProperties(prefix = "xxx")对象、List、Map、嵌套结构类必须被 Spring 管理(加 @Component)

注意事项:

坑表现解决
yml 冒号后漏空格配置不生效,key 不高亮写成 port: 9090 这种“冒号 + 空格”的形式
@Value 里写字段名Could not resolve placeholder写配置里的 key 全名,如 ${mykey.key1}
@ConfigurationProperties 类没被管理字段全是默认值(0 / null)加 @Component
prefix 大小写不对绑定失败建议全小写,和 yml 第一层 key 一致
properties 和 yml 冲突生效的是 properties只用一种格式,别两个都留
单引号里的 \n不换行想要换行就用双引号



 以上就是 SpringBoot 配置文件的全部内容啦,从“为什么要有配置文件”一直写到“验证码实战”

如果这篇笔记对你有帮助,欢迎点赞 👍 + 收藏 ⭐ + 留言 📝,你的支持是我持续更新的最大动力!笔记中有任何错误或者表述不清的地方,也欢迎在评论区指出,我们一起进步 🚀

在这里插入图片描述

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/2502_93799892/article/details/167911835

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--