六、 SpringBoot 配置⽂件
- 本节⽬标
- 1. 配置⽂件作⽤
- 2. 配置⽂件快速⼊⼿
- 3. 配置⽂件的格式
- 4. properties 配置⽂件说明
- 4.1 properties 基本语法
- 4.2 读取配置⽂件
- 4.3 properties 缺点分析
- 5. yml 配置⽂件说明
- 5.1 yml 基本语法
- 5.2 yml 使⽤进阶
- 5.2.1 yml 配置不同数据类型及 null
- 5.2.1.1 yml 配置读取
- 5.2.1.2 注意事项:value 值加单双引号 ★
- 5.2.2 配置对象 @ConfigurationProperties(prefix = "student") ★
- 5.2.3 配置集合@ConfigurationProperties("dbtypes") ★
- 5.2.4 配置Map ★
- maptypes: {map: {k1: kk1,k2: kk2, k3: kk3}}
- 5.3 yml优缺点
- 6. 综合性练习
- 6.1 验证码案例
- 6.1.1 Kaptcha 插件介绍
- 配置后, 可以直接访问 http://XXXX:port/home/captcha即可⽣成验证码,且自动存储在session中
- 6.1.2 需求
- 验证码前端代码
- 后端
- 配置文件
- 导包
- 代码
本节⽬标
- 学习SpringBoot配置⽂件的格式以及对应的语法
- 了解两个配置⽂件格式的差异
1. 配置⽂件作⽤
配置⽂件主要是为了解决编码带来的问题, 把可能会发⽣改变的信息, 放在⼀个集中的地⽅, 当我们启动某个程序时, 应⽤程序从配置⽂件中读取数据, 并加载运⾏.
硬编码是将数据直接嵌⼊到程序或其他可执⾏对象的源代码中, 也就是我们常说的"代码写死".
⽐如⼿机字体⼤⼩
如果采⽤硬编码的⽅式, 就直接在程序中指定字体⼤⼩, 所有的⽤⼾使⽤的都是同⼀个字体⼤⼩
但是不同的⽤⼾有不同的偏好, 我们可以把⼿机字体的⼤⼩放在配置⽂件中, 当程序启动时, 读取配置, 以⽤⼾设置的字体⼤⼩来显⽰.
使⽤配置⽂件, 可以使程序完成⽤⼾和应⽤程序的交互, 或者应⽤程序与其他应⽤程序的交互
- SpringBoot配置⽂件
SpringBoot⽀持并定义了配置⽂件的格式, 也在另⼀个层⾯达到了规范其他框架集成到SpringBoot的⽬的.
很多项⽬或者框架的配置信息也放在配置⽂件中, ⽐如:
- 项⽬的启动端⼝
- 数据库的连接信息(包含⽤⼾名和密码的设置)
- 第三⽅系统的调⽤密钥等信息
- ⽤于发现和定位问题的普通⽇志和异常⽇志等
项⽬的启动端⼝
SpringBoot内置了Tomcat服务器, 默认端⼝号是8080, 但是⽤⼾电脑上8080端⼝号有可能就被其他应⽤程序占⽤了, 所以SpringBoot需要⽀持让⽤⼾⾃定义端⼝号数据库连接信息
为了更⽅便简单的访问数据库, 出现了⼀些持久层框架, 其实就是对JDBC进⾏了更深层次的封装.让⽤⼾通过简单⼏⾏代码就可完成数据库的访问. 但是不同的应⽤程序访问的数据库不同, 这些持久层框架就需要⽀持⽤⼾可以⾃定义配置数据库的连接信息.
2. 配置⽂件快速⼊⼿
我们在前⾯讲了Tocmat 默认端⼝号是8080, 所以我们程序访问时的端⼝号也是8080
但是如果8080端⼝号已经被其他进程使⽤了呢? 我们可以通过配置⽂件来修改服务的端⼝号SpringBoot 在创建项⽬时, 就已经帮我们创建了配置⽂件
修改 application.properties ⽂件
server.port=9090
3. 配置⽂件的格式
Spring Boot 配置⽂件有以下三种:
• application.properties
• application.yml
• application.yaml
yml 为yaml的简写, 实际开发中出现频率最⾼. yaml 和yml 的使⽤⽅式⼀样, 课堂中只讲yml⽂件的使⽤
当应⽤程序启动时, Spring Boot会⾃动从classpath路径找到并加载application.properties 和 application.yaml 或者application.yml ⽂件.
也可以通过spring.config.name指定⽂件路径和名称, 参考 https://docs.spring.io/spring-boot/reference/features/external-config.html#features.external-config.files
- 理论上讲 .properties 和 .yml 可以并存在于⼀个项⽬中,当 .properties 和 .yml并存时,两个配置都会加载. 如果配置⽂件内容有冲突, 则以 .properties 为主, 也就是 .properties 优先级更⾼.
- 虽然理论上来讲 .properties 可以和 .yml 共存,但实际的业务当中,我们通常会采取⼀种统⼀的配置⽂件格式,这样可以更好的维护(降低故障率)
4. properties 配置⽂件说明
properties 配置⽂件是最早期的配置⽂件格式,也是创建 SpringBoot 项⽬默认的配置⽂件
4.1 properties 基本语法
properties 是以键值的形式配置的,key 和 value 之间是以"="连接的,如
#配置项目端口号
server.port=8080
#配置数据库连接信息
spring.datasource.url=idbc:mysql://127.0.0.1:3306/testdb?characterEncoding=utf8&spring.datasource.username=root
spring.datasource.password=root
4.2 读取配置⽂件
如果在项⽬中,想要主动的读取配置⽂件中的内容,可以使⽤ @Value 注解来实现。
@Value 注解使⽤" ${} "的格式读取,如下代码所⽰:
properties 配置如下:
mykey.key1 = bite
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;
}
}
最终执⾏效果:
4.3 properties 缺点分析
properties 配置是以 key-value 的形式配置的,如下图所⽰:
从上述配置key看出,properties 配置⽂件中会有很多的冗余的信息,⽐如这些:
想要解决这个问题,就可以使⽤ yml 配置⽂件的格式化
5. yml 配置⽂件说明
yml 是 YAML 是缩写,它的全称 Yet Another Markup Language 翻译成中⽂就是“另⼀种标记语⾔.我们先来学习yml的语法
5.1 yml 基本语法
yml 是树形结构的配置⽂件,它的基础语法是"key: value".
key 和 value 之间使⽤英⽂冒号加空格的⽅式组成,空格不可省略基础语法如下:
第⼀项的配置为正确的,key 也是⾼亮显⽰的. 第⼆项没有空格是错误的使⽤⽅式,第⼆项的 key 也没有⾼亮显⽰
使⽤ yml 连接数据库
yml 使⽤⽰例
spring:
datasource:
url: jdbc:mysql://127.0.0.0:3306/dbname?characterEncoding=utf8&usesSL=false
username:root
password:root
5.2 yml 使⽤进阶
5.2.1 yml 配置不同数据类型及 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: ''
5.2.1.1 yml 配置读取
yml 读取配置的⽅式和 properties 相同,使⽤ @Value 注解即可,实现代码如下:yml配置:
string:
hello: bite
@RestController
public class ReadYml {
@Value("${string.hello}")
private String hello;
@Requestapping("/ymlkey")
public string key() {
return"读取到值:"+hello;
}
}
5.2.1.2 注意事项:value 值加单双引号 ★
字符串默认不⽤加上单引号或者双引号,如果加英⽂的单双引号可以表⽰特殊的含义。
尝试在 application.yml 中配置如下信息
string:
strl: Hello \n Spring Boot.
str2: 'Hello in Spring Boot.'
str3: "Hello \n Spring Boot."
@RestController
public class ReadYml {
@Value("${string.strl}")
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";
}
}
从上述结果可以看出:
• 字符串默认不⽤加上单引号或者双引号。
• 单引号会转义特殊字符,使其失去特殊功能, 始终是⼀个普通的字符串.
• 双引号不会转义字符串⾥⾯的特殊字符, 特殊字符会表⽰本⾝的含义.
此处的转义理解起来会有些拗⼝, \n 本意表⽰的是换⾏
使⽤单引号会转义, 就是说, \n 不再表⽰换⾏了, ⽽是表⽰⼀个普通的字符串使⽤双引号不会转义, 表⽰ \n 表⽰的是它本⾝的含义, 就是换⾏
5.2.2 配置对象 @ConfigurationProperties(prefix = “student”) ★
我们还可以在 yml 中配置对象,如下配置:
student:
id: 1
name: Java
age: 18
或者是使⽤⾏内写法(与上⾯的写法作⽤⼀致):
student: {id: 1,name: Java,age: 18}
这个时候就不能⽤ @Value 来读取配置中的对象了,此时要使⽤另⼀个注解
@ConfigurationProperties 来读取,具体实现如下:
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@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();
}
}
5.2.3 配置集合@ConfigurationProperties(“dbtypes”) ★
配置⽂件也可以配置 list 集合,如下所⽰:
dbtypes:
name:
- mysql
- sqlserver
- db2
集合的读取和对象⼀样,也是使⽤ @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();
}
}
5.2.4 配置Map ★
maptypes: {map: {k1: kk1,k2: kk2, k3: kk3}}
配置⽂件也可以配置 map,如下所⽰:
maptypes:
map:
k1: kk1
k2: kk2
k3: kk3
或者是使⽤⾏内写法(与上⾯的写法作⽤⼀致):
maptypes: {map: {k1: kk1,k2: kk2, k3: kk3}}
Map的读取和对象⼀样,也是使⽤ @ConfigurationProperties 来读取的,具体实现如下
@Component
@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();
}
}
5.3 yml优缺点
优点:
- 可读性⾼,写法简单, 易于理解
- ⽀持更多的数据类型, 可以简单表达对象, 数组, List,Map等数据形态.
- ⽀持更多的编程语⾔, 不⽌是Java中可以使⽤, 在Golang, Python, Ruby, JavaScript中也可以使⽤
缺点:
- 不适合写复杂的配置⽂件
- 对格式有较强的要求(⼀个空格可能会引起⼀场⾎案
6. 综合性练习
6.1 验证码案例
随着安全性的要求越来越⾼, ⽬前项⽬中很多都使⽤了验证码, 验证码的形式也是多种多样, 更复杂的图形验证码和⾏为验证码已经成为了更流⾏的趋势.
验证码的实现⽅式很多, ⽹上也有⽐较多的插件或者⼯具包可以使⽤, 咱们选择使⽤Google的开源项⽬Kaptcha来实现.
6.1.1 Kaptcha 插件介绍
Kaptcha 是Google的⼀个⾼度可配置的实⽤验证码⽣成⼯具. 代码:
http://code.google.com/p/kaptcha/
⽹上有很多⼈甚⾄公司基于Google的kaptcha进⾏了⼆次开发. 我们选择⼀个直接适配SpringBoot的开源项⽬
https://github.com/oopsguy/kaptcha-spring-boot
由于作者的⽂档写的不是很全, 下⾯简单介绍下插件的使⽤
-
原理
验证码可以客⼾端⽣成, 也可以服务器⽣成. 对于普通的字符验证码, 后端通常分两部分.
⼀是⽣成验证码内容, 根据验证码内容和⼲扰项等, ⽣成图⽚, 返回给客⼾端
⼆是把验证码内容存储起来, 校验时取出来进⾏对⽐.kaptcha插件选择把验证码存储在Session⾥. -
引⼊依赖
-
⽣成验证码
该插件提供了两种⽅式⽣成验证码
• 通过代码来生成(参考文档: https://github.com/oopsguy/kaptcha-spring-
boot/blob/master/README zh-CN.md,不再介绍)
• 仅通过配置⽂件来⽣成验证码(推荐)
kaptcha:
text-producer:
character:
length: 4
font:
color: blue
items:
# admin captcha
admin:
path: /admin/captcha
session:
key: KAPTCHA_SESSION_KEY
date: KAPTCHA_SESSION_DATE
配置后, 可以直接访问 http://XXXX:port/home/captcha即可⽣成验证码,且自动存储在session中
6.1.2 需求
界⾯如下图所⽰
- ⻚⾯⽣成验证码
- 输⼊验证码, 点击提交, 验证⽤⼾输⼊验证码是否正确, 正确则进⾏⻚⾯跳转
验证码前端代码
<!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="/admin/captcha" 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', '/admin/captcha?dt=' + new Date().getTime()).fadeIn();
});
$("#checkCaptcha").click(function () {
$.ajax({
type:"get",
url: "/admin/check",
data:{
inputCaptcha: $("#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>
后端
配置文件
kaptcha:
text-producer:
character:
length: 4
font:
color: blue
items:
# admin captcha
admin:
path: /admin/captcha
session:
key: KAPTCHA_SESSION_KEY
date: KAPTCHA_SESSION_DATE
导包
<dependency>
<groupId>com.oopsguy.kaptcha</groupId>
<artifactId>kaptcha-spring-boot-starter</artifactId>
<version>1.0.0-beta-2</version>
</dependency>
代码
package com.bite.captcha;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpSession;
import java.util.Date;
@RequestMapping("/admin")
@RestController
public class KaptchaController {
private static final String KAPTCHA_SESSION_KEY = "KAPTCHA_SESSION_KEY";
private static final String KAPTCHA_SESSION_DATE = "KAPTCHA_SESSION_DATE";
private static final long TIME_OUT = 60*1000;//一分钟, 毫秒数
/**
* 校验验证码是否正确
* @param inputCaptcha 用户输入的验证码
* @return
*/
@RequestMapping("/check")
public boolean check(String inputCaptcha, HttpSession session){
//1. 判断输入的验证码是否为空
//2. 获取生成的验证码
//3. 比对 生成的验证码和输入的验证码是否一致
//4. 确认验证码是否过期
if (!StringUtils.hasLength(inputCaptcha)){
return false;
}
//生成的验证码(正确的验证码)
String saveCaptcha = (String)session.getAttribute(KAPTCHA_SESSION_KEY);
Date savaCaptchaDate = (Date)session.getAttribute(KAPTCHA_SESSION_DATE);
if (inputCaptcha.equalsIgnoreCase(saveCaptcha)){//不区分大小写
if (savaCaptchaDate!=null || System.currentTimeMillis()-savaCaptchaDate.getTime()<TIME_OUT){
return true;
}
}
return false;
}
}