计算机上有数以千计的配置文件,我们使用的绝大多数软件,比如浏览器,微信,Idea,甚至电脑,手机,都离不开配置文件,我们可能永远不会直接与其中的大部分文件打交道,但它们确实以不同的形式散落在我们的计算机上,比如C:\Users, C:(Windows文件夹,以及各种*.config,*.xml文件
配置文件主要是为了解决硬编码带来的问题,把可能会发生改变的信息,放在一个集中的地方,当我们启动某个程序时,应用程序从配置文件中读取数据,并加载运行.
硬编码是将数据直接嵌入到程序或其他可执行对象的源代码中,也就是我们常说的”代码写死” 比如手机字体大小 如果采用硬编码的方式,就直接在程序中指定字体大小,所有的用户使用的都是同一个字体大小 但是不同的用户有不同的偏好,我们可以把手机字体的大小放在配置文件中,当程序启动时,读取配 置,以用户设置的字体大小来显示
使用配置文件,可以使程序完成用户和应用程序的交互,或者应用程序与其他应用程序的交互
SpringBoot配置文件
SpringBoot支持并定义了配置文件的格式,也在另一个层面达到了规范其他框架集成到SpringBoot的目的。
很多项目或者框架的配置信息也放在配置文件中,比如:
项目的启动端口 SpringBoot内置了Tomcat服务器,默认端口号是8080,但是用户电脑上8080端口号有可能就被其他 应用程序占用了,所以SpringBoot需要支持让用户自定义端口号 数据库连接信息 为了更方便简单的访问数据库,出现了一些持久层框架,其实就是对JDBC进行了更深层次的封装,让用 户通过简单几行代码就可完成数据库的访问,但是不同的应用程序访问的数据库不同,这些持久层框 架就需要支持用户可以自定义配置数据库的连接信息。
SpringBoot配置文件有以下三种:
yml为yaml的简写,实际开发中出现频率最高.yaml和yml的使用方式一样 当应用程序启动时,SpringBoot会自动从classpath路径找到并加载 application.properties和application.yaml或者application.yml文件.

类似商品的包装一样,有新老两款包装.properties类型的配置文件就属于老款包装,也是创建SpringBoot项目时默认的文件格式(主要是因为仓库里还有库存),而yml属于新版包装,如果用户了解情况直接指定要新款包装,那么就直接发给他
.properties和.yml可以并存在一个项目中,当.properties和.yml并存时,两个配置都会加载,如果配置文件内容有冲突,则以.properties为主,也就是.properties优先级更高
.properties可以和.yml共存,但实际的业务当中,我们通常会采取一种统一的配置文件格式,这样可以更好的维护(降低故障率)
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
#自定义配置
my.key=test如果在项目中,想要主动的读取配置文件中的内容,可以使用@Value注解来实现。
@Value注解使用"${}"的格式读取,如下代码所示:
package com.example.demo.controller;
import jakarta.annotation.PostConstruct;
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("${my.key}")
private String myKey;
@RequestMapping("/readValue")
public String readValue(){
return "从配置文件中获取信息" + myKey;
}
@PostConstruct
public void readValue2(){
System.out.println(myKey);
}
}类型会自动进行转换
#自定义配置
my.key=test
my.key2=5
my.key3=true@RestController
public class propertiesController {
@Value("${my.key}")
private String myKey;
@Value("${my.key2}")
private Integer myKey2;
@Value("${my.key3}")
private Boolean myKey3;
@PostConstruct
public void readValue2(){
System.out.println(myKey);
}
@PostConstruct
public void readValue3(){
System.out.println(myKey2);
}
@PostConstruct
public void readValue4(){
System.out.println(myKey3);
}
}
properties配置文件中会有很多的冗余的信息,比如这些:

yml是YAML是缩写,它的全称Yet Another Markup Language翻译成中文就是“另一种标记语言.
yml是树形结构的配置文件,它的基础语法是"key:value"
key和value之间使用英文冒号加空格的方式组成,空格不可省略
基础语法如下:
spring:
datasource:
url: dbc:mysql://127.0.0.1:3306/testdb?characterEncoding=utf8&useSSL=false
username: root
password: root@PostConstruct 注解的方法会在 Bean 的依赖注入完成后(所有 @Value 注入后)自动调用一次
@Value 是依赖注入的一种具体实现,注入目标为基本数据类型(如 String、int),注入配置文件中的值
package com.example.demo.controller;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class YmlController {
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@PostConstruct
public void readValue(){
System.out.println(url);
System.out.println(username);
System.out.println(password);
}
}
优点:
缺点:
# 字符串
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:
str1: Hello \n Spring Boot.
str2: 'Hello \n Spring Boot.'
str3: "Hello \n Spring Boot."package com.example.demo.controller;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RestController;
@Controller
public class YmlController {
@Value("${string.str1}")
private String str1;
@Value("${string.str2}")
private String str2;
@Value("${string.str3}")
private String str3;
@PostConstruct
public void readValue(){
System.out.println(str1);
System.out.println(str2);
System.out.println(str3);
}
}
此处的转义理解起来会有些拗口,\n本意表示的是换行 使用单引号会转义,就是说,\n不再表示换行了,而是表示一个普通的字符串 使用双引号不会转义,表示\n表示的是它本身的含义,就是换行 JavaEE的学习重在理解和使用,不纠结概念和描述
student:
name: Java
id: 1
age: 18package com.example.demo.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
@ConfigurationProperties(prefix = "student")
public class StudentConfig {
private String name;
private Integer id;
private Integer age;
}@RestController
public class YmlController {
@Autowired
public StudentConfig studentConfig;
@RequestMapping("/readObject")
public String readObject() {
return studentConfig.toString();
}
}
hobbies:
name:
- rap
- basketball
- singpackage com.example.demo.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.List;
@Data
@Configuration
@ConfigurationProperties(prefix = "hobbies")
public class ListConfig {
private List<String> name;
}@RestController
public class YmlController {
@Autowired
public ListConfig listConfig;
@RequestMapping("/readList")
public String readList() {
return listConfig.toString();
}
}
@ConfigurationProperties 的映射规则: 必须通过 prefix + 类属性名 精确匹配 YAML 中的层级结构。 例如:prefix = "hobbies" + name 属性 → 对应 hobbies.name。
hobbies 直接是列表(如 - sing),则类中需用 private List<String> hobbies; 直接映射,且 prefix 设为父级(如 prefix = "")。
@Data
@Configuration
@ConfigurationProperties(prefix = "")
public class ListConfig {
private List<String> hobbies;
}hobbies:
- dance
- rap
- basketball
- sing
maptypes:
map:
k1: kk1
k2: kk2
k3: kk3
maptypes: {map: {k1: kk1,k2: kk2, k3: kk3}} @Configuration
@ConfigurationProperties("maptypes")
@Data
public class MapConfig {
private HashMap<String,String> map;
}@RestController
public class ReadYml2 {
@Autowired
private MapConfig mapConfig;
@RequestMapping("/readMap")
public String readStudent(){
return mapConfig.toString();
}
}验证码的实现方式很多,可以前端实现,也可以后端实现,网上也有比较多的插件或者工具包可以使用,
咱们选择使用Hutool提供的小工具来实现
界面如下图所示

需求分析
后端需要提供两个服务
接口定义
请求:
请求URL: /captcha/getCaptcha 响应:验证码图片内容
浏览器给服务器发送一个
/captcha/getCaptcha这样的请求,服务器返回一个图片,浏览器显示在页面上
请求:/captcha/check
请求URL: /captcha/check
请求参数: captcha=sfvrcaptcha:用户输入的验证码
响应:true根据用户输入的验证码,校验验证码是否正确.true:验证成功.false:验证失败.
Hutool是一个Java工具包类库,对文件、流、加密解密、转码、正则、线程、XML等JDK方法进行封
装,组成各种Util工具类。
Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效
率,使Java拥有函数式语言般的优雅,让Java语言也可以"甜甜的".
Hutool官网:https://hutool.cn/
Hutool参考文档:https://hutool.cn/docs/#/
Hutool源码:https://github.com/dromara/hutool
captcha:
width: 100
height: 40
session:
key: SESSION_CAPTCHA_KEY
date: SESSION_CAPTCHA_DATEpackage com.example.captchademo.model;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
@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;
}
}不加public:
不同包中的非子类,只能用public访问


package com.example.captchademo.controller;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import com.example.captchademo.model.CaptchaProperties;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.util.Date;
@RestController
@RequestMapping("/captcha")
public class CaptchaController {
@Autowired
private CaptchaProperties captchaProperties;
@GetMapping("/getCaptcha")
public void getCaptcha(HttpServletResponse response,HttpSession session) {
//生成验证码
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(captchaProperties.getWidth(), captchaProperties.getHeight());
String code = lineCaptcha.getCode();
System.out.println(code);
//设置session
//将当前时间存入session
session.setAttribute(captchaProperties.getSession().getDate(), new Date());
session.setAttribute(captchaProperties.getSession().getKey(), code);
//把验证码加到浏览器
try {
//把验证码写到浏览器
lineCaptcha.write(response.getOutputStream());
response.setContentType("image/jpeg");
response.setCharacterEncoding("UTF-8");
response.setHeader("Cache-Control", "no-cache");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@RequestMapping("/check")
//前端给定数据 String captcha接收
public Boolean check(String captcha,HttpSession session) {
if (!StringUtils.hasText(captcha)) {
return false;
}
String code = (String) session.getAttribute(captchaProperties.getSession().getKey());
Date date = (Date) session.getAttribute(captchaProperties.getSession().getDate());
//一分钟
long VALID_TIME_OUT = 60 * 1000;
if (date == null || System.currentTimeMillis()-date.getTime() > VALID_TIME_OUT) {
return false;
}
//校验Captcha是否正确
return captcha.equalsIgnoreCase(code);
}
}<!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 () {
$.ajax({
type: "post",
url: "/captcha/check",
data: {
captcha: $("#inputCaptcha").val()
},
success: function(result){
if (result){
location.href = "success.html"
}else {
alert("验证码错误");
}
}
});
});
</script>
</body>
</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>