欢迎关注个人主页:逸狼
创造不易,可以点点赞吗
如有错误,欢迎指出~
目录
[举例: 通过配置文件修改端口号](#举例: 通过配置文件修改端口号)
[编辑 yml优缺点](#编辑 yml优缺点)
[1. ⽣成验证码](#1. ⽣成验证码)
[2. 校验验证码是否正确](#2. 校验验证码是否正确)
配置⽂件主要是为了解决硬编码带来的问题 ,把可能会发⽣改变的信息,放在⼀个集中的地⽅, 当我们启 动某个程序时,应⽤程序从配置⽂件中读取数据,并加载运⾏.
硬编码是将数据直接嵌⼊到程序或其他可执⾏对象的源代码中,也就是我们常说的"代码写死"
使⽤配置⽂件,可以使程序完成⽤⼾和应⽤程序的交互,或者应⽤程序与其他应⽤程序的交互
SpringBoot配置⽂件
SpringBoot⽀持并定义了配置⽂件的格式,也在另⼀个层⾯达到了规范其他框架集成到SpringBoot的 ⽬的.
很多项⽬或者框架的配置信息也放在配置⽂件中,⽐如:
• 项⽬的启动端⼝
• 数据库的连接信息(包含⽤⼾名和密码的设置)
• 第三⽅系统的调⽤密钥等信息
• ⽤于发现和定位问题的普通⽇志和异常⽇志等.
举例: 通过配置文件修改端口号
Tocmat默认端⼝号是8080,所以我们程序访问时的端⼝号也是8080 但是如果8080端⼝号已经被其他进程使⽤ ,我们可以通过配置⽂件来修改服务的端⼝号 SpringBoot在创建项⽬时,就已经帮我们创建了配置⽂件

修改 application.properties ⽂件 加上server.port=9090


配置⽂件的格式
SpringBoot配置⽂件有以下三种:
- application.properties
- application.yml
- application.yaml
yml 为yaml的简写,实际开发中出现频率最⾼ .yaml和yml的使⽤⽅式⼀样, 当应⽤程序启动时,SpringBoot会**⾃动从classpath路径找到并加载** application.properties 和application.yaml 或者application.yml ⽂件.
- 理论上讲**.properties 和.yml 可以并存** 在于⼀个项⽬中,当.properties 和.yml 并存时,两个配置都会加载 .如果配置⽂件内容有冲突,则以.properties 为主,也就是 .properties 优先级更⾼.( properties配置⽂件是最早期的配置⽂件格式,也是创建SpringBoot项⽬默认的配置⽂件 )
- 虽然理论上来讲.properties 可以和.yml 共存,但实际的业务 当中,我们通常会采取⼀种 统⼀的配置 ⽂件格式,这样可以更好的维护(降低故障率).
properties基本语法
properties是以键值的形式配置的,key和value之间是以"="连接的,如: # 配置项⽬端⼝号 server.port=8080
配置⽂件中使⽤"#"来添加注释信息。
读取配置⽂件
如果在项⽬中,想要主动的读取配置⽂件 中的内容,可以使⽤@Value 注解来实现。 @Value 注解使⽤" ${} "的格式读取,如下代码所⽰:
properties配置如下:
m.k=do
package com.example.demo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
//@RequestMapping("/p1")
public class PropertiesController {
@Value("${m.k}")
private String k;
@RequestMapping("/v1")
public String key(String input){//参数名 input 与成员变量 k 同名,否则可能会读取失败
System.out.println("注入后的key值为: " + k);
if (k == null) {
System.out.println("key的值为null");
}
return "读取到properties文件中的k的值: "+ k;
}
}

properties配置文件的缺点
properties配置⽂件中会有很多的冗余的信息,⽐如

yml配置文件可以解决以上问题
yml配置⽂件
yml是YAML是缩写,它的全称YetAnotherMarkupLanguage翻译成中⽂就是"另⼀种标记语⾔.
yml基本语法
yml是树形结构的配置⽂件,它的基础语法是**"key:value".** key和value之间使⽤英⽂冒号加空格 的⽅式组成,空格不可省略

正确配置 会有高亮提示
yml和properties连接数据库的配置对⽐

yml的使用
# 字符串
string.value: Hello
# 布尔值,true或false
boolean.value: true
boolean.value1: false
# 整数
int.value: 10
# 浮点数
float.value: 3.14159
# Null,~代表null
null.value: ~
# "" 空字符串
#, 直接后⾯什么都不加就可以了, 但这种⽅式不直观, 更多的表⽰是使⽤引号括起来
empty.value: ''
yml配置读取
yml读取配置的⽅式和properties 相同,使⽤@Value注解即可,实现代码如下:
@Value("${code.java}")
private String java;
@RequestMapping("/v2")
public String v2(){
return "读取yml配置文件code.java的值: "+ java;
}

注意事项:value值加单双引号 字符串默认不⽤加上单引号或者双引号,如果加英⽂的单双引号可以表⽰特殊的含义。
尝试在application.yml中配置如下信息:
String:
str1: hello \n yml
str2: 'hello \n yml'
str3: "hello \n yml"
@RequestMapping("/v3")
public String v3(){
System.out.println(str1);
System.out.println(str2);
System.out.println(str3);
return "yml";
}

从上述结果可以看出:
- • 字符串默认不⽤加上单引号或者双引号。
- • 单引号会转义特殊字符,使其失去特殊功能,始终是⼀个普通的字符串.
- • 双引号不会转义字符串⾥⾯的特殊字符,特殊字符会表⽰本⾝的含义.
配置对象
我们还可以在yml中配置对象,如下配置:以下两者写法都行
Student:
id: 1
name: zhangsan
age: 21
Student2: {id: 2, name: lisi, age: 18}
读取yml配置文件中的对象
这个时候就不能⽤@Value来读取配置中的对象了,此时要使⽤另⼀个注解**@ConfigurationProperties (prefix = "对应的是配置文件中的对象")**来读取,具体实现如下:
package com.example.demo;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@ConfigurationProperties(prefix = "student")//注意对象名称
@Data
@Component
public class Student {
public int id;
public String name;
public int age;
}
@Autowired
private Student student;
@RequestMapping("/v4")
public String v4(){
return student.toString();
}

配置集合
2022:
name:
- zhangsan
- lisi
- wangwu
读取yml配置文件中的集合
集合的读取和对象⼀样,也是使⽤@ConfigurationProperties 来读取的,具体实现如下:
package com.example.demo;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
@Data
@Component
@ConfigurationProperties(prefix = "2022")
public class List2020 {
private List<String> name;
}
@Autowired
private List2020 list2020;
@RequestMapping("/v5")
public String v5(){
return list2020.toString();
}

配置Map
配置⽂件也可以配置map,如下所⽰
maptypes:
map:
k1: 111
k2: 222
k3: 333
行内写法作用相同
maptypes2: {map2: {k1: 1, k2: 2, k3: 3}}
Map的读取和对象⼀样,也是使⽤@ConfigurationProperties 来读取的,具体实现如下:
package com.example.demo;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.HashMap;
@Data
@Component
@ConfigurationProperties(prefix = "maptypes")
public class MapTypes {
public HashMap<String, String> map;
}
@Autowired
private MapTypes mapTypes;
@RequestMapping("/v6")
public String v6(){
return mapTypes.toString();
}
yml优缺点
优点:
-
可读性⾼,写法简单,易于理解
-
⽀持更多的数据类型,可以简单表达对象,数组,List,Map等数据形态.
-
⽀持更多的编程语⾔,不⽌是Java中可以使⽤,在Golang,Python,Ruby,JavaScript中也可以使⽤
缺点:
- 不适合写复杂的配置⽂件

转换的过程也⽐较花费精⼒,如果配置更复杂⼀点,可读性会更差,代码也会更难写
- 对格式有较强的要求(⼀个空格可能会引起⼀场⾎案)
验证码案例
验证码的实现⽅式很多,可以前端实现,也可以后端实现.⽹上也有⽐较多的插件或者⼯具包可以使⽤, 咱们选择使⽤Hutool提供的⼩⼯具来实现
需求
界⾯如下图所⽰
-
⻚⾯⽣成验证码
-
输⼊验证码,点击提交,验证⽤⼾输⼊验证码是否正确,正确则进⾏⻚⾯跳转

约定前后端交互接⼝
需求分析 后端需要提供两个服务
-
⽣成验证码,并返回验证码
-
校验验证码是否正确:校验验证码是否正确.
接⼝定义
1. ⽣成验证码
请求: 请求URL: /captcha/getCaptcha
响应:验证码图⽚内容
浏览器给服务器发送⼀个 /captcha/getCaptcha 这样的请求,服务器返回⼀个图⽚,浏览器显 ⽰在⻚⾯上
2. 校验验证码是否正确
请求:/captcha/check 请求URL: /captcha/check 请求参数: captcha=xn8d
captcha:⽤⼾输⼊的验证码
响应: true 根据⽤⼾输⼊的验证码,校验验证码是否正确.
true:验证成功.false:验证失败.
Hutool⼯具介绍
这里验证码的实现,使⽤Hutool提供的⼩⼯具来实现 Hutool是⼀个Java⼯具包类库,对⽂件、流、加密解密、转码、正则、线程、XML等JDK⽅法进⾏封 装,组成各种Util⼯具类. Hutool是⼀个⼩⽽全的Java⼯具类库,通过静态⽅法封装,降低相关API的学习成本,提⾼⼯作效 率,使Java拥有函数式语⾔般的优雅,让Java语⾔也可以"甜甜的".

引入依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-captcha</artifactId>
<version>5.8.22</version>
</dependency>
1.获取和验证验证码
package com.example.demo.captcha;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
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;
@RequestMapping("/captcha")
@RestController
public class HuToolCaptchaController {
@GetMapping("/getCaptcha")
public void getCaptcha(HttpServletResponse response, HttpSession session){
//定义图形验证码的长和宽
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(100, 40, 4, 150);
String code = lineCaptcha.getCode();
//存储session
session.setAttribute("CAPTCHA_KEY",code);
//获取当前时间, 验证码的时效性
session.setAttribute("CAPTCHA_Date",new Date());
//验证码写出,可以写到文件,也可以写到流里
try {
lineCaptcha.write(response.getOutputStream());
//输出
System.out.println("生成的验证码: "+ lineCaptcha.getCode());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@RequestMapping("/check")
public boolean check(HttpSession session, String captcha){
//captcha为空,直接返回false;
if(!StringUtils.hasLength(captcha)){
return false;
}
String code = (String) session.getAttribute("CAPTCHA_KEY");
return captcha.equals(code);
}
}


对程序进⾏调整
1)把配置项挪到配置⽂件中
2)把⽣成的验证码存储在Session中,校验时使⽤
yml文件配置项
captcha:
height: 40
width: 100
session:
key: CAPTCHA_SESSION_KEY
date: KAPTCHA_SESSION_DATE
验证码配置项对应的Java对象
package com.example.demo.captcha;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
@Data
@Configuration
@ConfigurationProperties(prefix = "captcha")
public class Captcha {
private Integer width;
private Integer height;
private Session session;
@Data
public static class Session{//要使用公开的 静态内部类,
// 否则session拿不到值(要先有外部类的对象,然后才能拿到内部类,静态内部类就不用), 或者不要写内部类
private String date;
private String key;
}
}
后端代码实现
package com.example.demo.captcha;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.Data;
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;
@RequestMapping("/captcha")
@RestController
public class HuToolCaptchaController {
//注入配置文件对应对象
@Autowired
private Captcha captcha;
//过期时间1分钟 = 60秒 * 1000毫秒
private static long VALID_TIME_OUT = 60 * 1000;
@GetMapping("/getCaptcha")
public void getCaptcha(HttpServletResponse response, HttpSession session){
//定义图形验证码的长和宽
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(captcha.getWidth(), captcha.getHeight());
String code = lineCaptcha.getCode();
//存储session
session.setAttribute(captcha.getSession().getKey(),code);
//当前时间
session.setAttribute(captcha.getSession().getDate(),new Date());
//验证码写出,可以写到文件,也可以写到流里
try {
lineCaptcha.write(response.getOutputStream());
response.setContentType("image/jpeg");
response.setCharacterEncoding("utf-8");
//防止缓存
response.setHeader("Pragma", "No-cache");
//输出
System.out.println("生成的验证码: "+ lineCaptcha.getCode());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@RequestMapping("/check")
public boolean check(HttpSession session, String captcha1){
System.out.println("用户输入验证码: "+ captcha1);
//captcha为空,直接返回false;
if(!StringUtils.hasLength(captcha1)){
return false;
}
String code = (String) session.getAttribute(captcha.getSession().getKey());
Date date = (Date) session.getAttribute(captcha.getSession().getDate());
if(date == null || (System.currentTimeMillis() - date.getTime()) > VALID_TIME_OUT){
return false;
}
//验证码不区分大小写验证验证码是否正确
return captcha1.equalsIgnoreCase(code);
}
}
前端代码实现
index.html
<!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: {
captcha1: $("#inputCaptcha").val()
},
success: function(result){
if(result){
location.href = "success.html"
}else{
alert("验证码错误");
}
}
});
});
</script>
</body>
</html>
success.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>
测试

总结
- properties是以 key=value 的形式配置的键值类型的配置⽂件,yml使⽤的是树形配置⽅式.
- 读取配置⽂件内容,使⽤@Value 注解,注解内使⽤**" ${} "**的格式读取.
- yml层级之间使⽤换⾏缩进的⽅式配置,key和value之间使⽤":"(英⽂冒号)加空格的⽅式设置,并 且空格不可省略.
- properties为早期并且默认 的配置⽂件格式,其配置存在⼀定的冗余数据,使⽤yml可以很好的解决 数据冗余的问题,但不适合复杂配置
- yml可以和properties共存,但⼀个项⽬中建议使⽤⼀种配置类型⽂件.