目录
- SpringBoot 配置⽂件
- 配置⽂件作⽤
- SpringBoot配置⽂件
- 配置⽂件快速⼊⼿
- 配置⽂件的格式
- properties 配置⽂件说明
- properties 基本语法
- 读取配置⽂件
- properties 缺点分析
- yml 配置⽂件说明
- yml 基本语法
- yml 使⽤进阶
- yml 配置不同数据类型及 null
- 配置对象
- 配置集合
- 配置Map
- yml优缺点
- 练习
- 验证码案例
- Kaptcha 插件介绍
- 需求
- 准备工作
- 约定前后端交互接口
- 实现服务器代码
- 调整前端⻚⾯代码
- 总结
SpringBoot 配置⽂件
- 学习SpringBoot配置⽂件的格式以及对应的语法
- 了解两个配置⽂件格式的差异
配置⽂件作⽤
热加载:写的代码,立即就被加载进来,生效了
热部署:如果代码发生了变动,就会重新部署
计算机上有数以千计的配置⽂件, 我们使⽤的绝⼤多数软件, ⽐如浏览器, 微信, Idea, 甚⾄电脑, ⼿机, 都离不开配置⽂件. 我们可能永远不会直接与其中的⼤部分⽂件打交道,但它们确实以不同的形式散落在我们的计算机上, ⽐如 C:\Users, C:\Windows⽂件夹, 以及各种 *.config, *.xml ⽂件
配置⽂件主要是为了解决硬编码带来的问题, 把可能会发⽣改变的信息, 放在⼀个集中的地⽅, 当我们启动某个程序时, 应⽤程序从配置⽂件中读取数据, 并加载运⾏.(简单一句话:解决硬编码的问题)
硬编码是将数据直接嵌⼊到程序或其他可执⾏对象的源代码中, 也就是我们常说的"代码写死"。
⽐如⼿机字体⼤⼩
如果采⽤硬编码的⽅式, 就直接在程序中指定字体⼤⼩, 所有的⽤⼾使⽤的都是同⼀个字体⼤⼩,但是不同的⽤⼾有不同的偏好, 我们可以把⼿机字体的⼤⼩放在配置⽂件中, 当程序启动时, 读取配置, 以⽤⼾设置的字体⼤⼩来显⽰
使⽤配置⽂件, 可以使程序完成⽤⼾和应⽤程序的交互, 或者应⽤程序与其他应⽤程序的交互
SpringBoot配置⽂件
SpringBoot的配置文件只有application
后缀可以是.yml
或者是.properties
不过这个名字可以改,但没必要改
SpringBoot⽀持并定义了配置⽂件的格式, 也在另⼀个层⾯达到了规范其他框架集成到SpringBoot的⽬的.
很多项⽬或者框架的配置信息也放在配置⽂件中, ⽐如:
- 项⽬的启动端⼝
- 数据库的连接信息(包含⽤⼾名和密码的设置)
- 第三⽅系统的调⽤密钥等信息
- ⽤于发现和定位问题的普通⽇志和异常⽇志等
项⽬的启动端⼝
SpringBoot内置了Tomcat服务器, 默认端⼝号是8080, 但是⽤⼾电脑上8080端⼝号有可能就被其他应⽤程序占⽤了, 所以SpringBoot需要⽀持让⽤⼾⾃定义端⼝号数据库连接信息
为了更⽅便简单的访问数据库, 出现了⼀些持久层框架, 其实就是对JDBC进⾏了更深层次的封装.让⽤⼾通过简单⼏⾏代码就可完成数据库的访问. 但是不同的应⽤程序访问的数据库不同, 这些持久层框架就需要⽀持⽤⼾可以⾃定义配置数据库的连接信息.
运行SpringBoot程序
- 打jar包
- 通过
java -jarXXX.jar
配置⽂件快速⼊⼿
我们在前⾯讲了Tocmat 默认端⼝号是8080, 所以我们程序访问时的端⼝号也是8080
但是如果8080端⼝号已经被其他进程使⽤了呢? 我们可以通过配置⽂件来修改服务的端⼝号
SpringBoot 在创建项⽬时, 就已经帮我们创建了配置⽂件
修改 application.properties
⽂件
server.port=9090
重新运⾏程序, 观察⽇志
访问程序: http://127.0.0.1:9090/login.html
此时: http://127.0.0.1:8080/login.html 就不能再访问了
配置⽂件的格式
Spring Boot 配置⽂件有以下三种:
- application.properties
- application.yml
- application.yaml
其实是事实是:SpringBoot只支持3个文件
yml 为yaml的简写, 实际开发中出现频率最⾼. yaml 和yml 的使⽤⽅式⼀样, 下面只讲yml⽂件的使用
当应⽤程序启动时, Spring Boot会⾃动从classpath路径找到并加载
application.properties
和application.yaml
或者application.yml
⽂件.也可以通过spring.config.name指定⽂件路径和名称, 参考 https://docs.spring.io/springboot/docs/current/reference/html/features.html#features.external-config.files
类似商品的包装⼀样, 有新⽼两款包装. properties 类型的配置⽂件就属于⽼款包装,也是创建Spring Boot 项⽬时默认的⽂件格式(主要是因为仓库⾥还有库存),⽽ yml 属于新版包装,如果⽤⼾了解情况直接指定要新款包装,那么就直接发给他
特殊说明
-
理论上讲
.properties
和.yml
可以并存在于⼀个项⽬中,当.properties
和.yml
并存时,两个配置都会加载. 如果配置⽂件内容有冲突, 则以.properties
为主, 也就是.properties
优先级更⾼。并不是.yml
会不生效 -
虽然理论上来讲
.properties
可以和.yml
共存,但实际的业务当中,我们通常会采取⼀种统⼀的配置⽂件格式(也就是只用其中一个文件就行),这样可以更好的维护(降低故障率)。现在都是用.yml
更多
properties 配置⽂件说明
properties 配置⽂件是最早期的配置⽂件格式,也是创建 SpringBoot 项⽬默认的配置⽂件
properties 基本语法
properties 是以键值的形式配置的,key 和 value 之间是以"="连接的,如:
# 配置项⽬端⼝号
server.port=8080
# 配置数据库连接信息
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/testdb?characterEncoding=utf8&
spring.datasource.username=root
spring.datasource.password=123456
# 自定义配置
demo.key1=hello.properties
key 的格式建议小写,单词之间用
.
进行分割PS:⼩技巧:配置⽂件中使⽤“
#
”来添加注释信息。更多可参考官网:https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#application-properties
读取配置⽂件
如果在项⽬中,想要主动的读取配置⽂件中的内容,可以使⽤ @Value
注解来实现。@Value
注解使⽤" ${}
"的格式读取,如下代码所⽰:
properties 配置如下:
# 自定义配置
demo.key1=hello.properties
@RestController
public class PropertiesController {
//读取配置文件
@Value("${demo.key1}")
private String key1;
@RequestMapping("/readKey")
public String readKey(){
return "读取到的配置项key1:"+key1;
}
}
最终执⾏效果:
${demo.key1}
如果去到$
就会把双引号的值赋给当前变量
properties 缺点分析
properties 配置是以 key-value 的形式配置的,如下图所⽰:
从上述配置key看出,properties 配置⽂件中会有很多的冗余的信息,⽐如这些:
想要解决这个问题,就可以使⽤ yml 配置⽂件的格式化了.
yml 配置⽂件说明
yml 是 YAML 是缩写,它的全称 Yet Another Markup Language 翻译成中⽂就是“另⼀种标记语⾔”.
我们先来学习yml的语法
yml 基本语法
yml 是树形结构的配置⽂件,它的基础语法是"key: value".
key 和 value 之间使⽤英⽂冒号加空格的⽅式组成,空格不可省略
基础语法如下:
第⼀项的配置为正确的,key 也是⾼亮显⽰的. 第⼆项没有空格是错误的使⽤⽅式,第⼆项的 key 也没有⾼亮显⽰
使⽤ yml 连接数据库
yml 使⽤⽰例:
# 修改端口号
server:
port: 9092
# 数据库相关配置
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/testdb?characterEncoding=utf8&useSSL=false
username: root
password: 123456
yml 和 properties 连接数据库的配置对⽐
yml 使⽤进阶
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: ''
yml 配置读取
yml 读取配置的⽅式和 properties 相同,使⽤ @Value
注解即可,实现代码如下:
yml配置:
demo:
key1: hello,yml
@RestController
public class YmlController {
@Value("${demo.key1}")
public String key1;
@RequestMapping("/readYml")
public String readYml(){
return key1;
}
}
访问: http://127.0.0.1:8080/readYml
运⾏结果:
demo:
key1: hello,yml
key2: hello,key2
key3: hello,key3
这里用了这个**@PostConstruct
注解,修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次**
@RestController
public class YmlController {
@Value("${demo.key1}")
public String key1;
@Value("${demo.key2}")
public String key2;
@Value("${demo.key3}")
public String key3;
@RequestMapping("/readYml")
public String readYml(){
return key1;
}
@PostConstruct
public void init(){
System.out.println("key1:"+key1);
System.out.println("key2:"+key2);
System.out.println("key3:"+key3);
}
}
注意事项:value 值加单双引号
字符串默认不⽤加上单引号或者双引号,如果加英⽂的单双引号可以表⽰特殊的含义。
尝试在 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";
}
}
以上程序的执⾏结果如下图所⽰:
从上述结果可以看出:
- 字符串默认不⽤加上单引号或者双引号
- 单引号会转义特殊字符,使其失去特殊功能, 始终是⼀个普通的字符串
- 双引号不会转义字符串⾥⾯的特殊字符, 特殊字符会表⽰本⾝的含义
此处的转义理解起来会有些拗⼝, \n 本意表⽰的是换⾏
使⽤单引号会转义, 就是说, \n 不再表⽰换⾏了, ⽽是表⽰⼀个普通的字符串
使⽤双引号不会转义, 表⽰ \n 表⽰的是它本⾝的含义, 就是换⾏
配置对象
- 配置
- 配置内容读取
- 注入
我们还可以在 yml 中配置对象,如下配置:
- 配置
Student:
id: 18
name: zhangsan
age: 12
或者是使⽤⾏内写法(与上⾯的写法作⽤⼀致):但可读性差
Student: {id: 18,name: zhangsan,age: 12}
这个时候就不能⽤ @Value
来读取配置中的对象了,此时要使⽤另⼀个注解@ConfigurationProperties
来读取,具体实现如下:
- 配置内容读取
@Component
@ConfigurationProperties(prefix = "student")
@Data
public class Student {
private Integer id;
private String name;
private Integer age;
}
调⽤类的实现如下:
- 注入
@Autowired
public Student student;
@PostConstruct
public void init(){
System.out.println("student:"+student);
}
运⾏结果如下:
配置集合
- 配置
- 配置内容读取
- 注入
配置⽂件也可以配置 list 集合,如下所⽰:
- 配置
dbtypes:
name:
- mysql
- sqlserver
- db2
记得一定要加空格,如果不加,这部分内容就会变成一个整体,统一赋值给对应属性
集合的读取和对象⼀样,也是使⽤ @ConfigurationProperties
来读取的,具体实现如下:
- 配置内容读取
注意这里的name要和配置里的name要对应上,dbtypes也要对应上
@Component
@ConfigurationProperties(prefix = "dbtypes")
@Data
public class DBType {
private List<String> name;
}
访问集合的实现如下:
- 注入
@Autowired
public DBType dbType;
@PostConstruct
public void init(){
System.out.println("dbtype:"+dbType);
}
运⾏结果如下:
配置Map
- 配置
- 配置内容读取
- 注入
配置⽂件也可以配置 map,如下所⽰:
- 配置
dbtypes:
name:
- mysql
- sqlserver
- db2
map:
k1: kk1
k2: kk2
k3: kk3
Map的读取和对象⼀样,也是使⽤ @ConfigurationProperties
来读取的,具体实现如下:
- 配置内容读取
注意这里的map要和配置里的map要对应上,dbtypes也要对应上
@Component
@ConfigurationProperties(prefix = "dbtypes")
@Data
public class DBType {
private List<String> name;
private HashMap<String,String> map;
}
打印类的实现如下:
- 注入
@Autowired
public DBType dbType;
@PostConstruct
public void init(){
System.out.println("dbtype:"+dbType);
}
运⾏结果如下:
yml优缺点
优点:
- 可读性⾼,写法简单, 易于理解
- ⽀持更多的数据类型, 可以简单表达对象, 数组, List,Map等数据形态.
- ⽀持更多的编程语⾔, 不⽌是Java中可以使⽤, 在Golang, Python, Ruby, JavaScript中也可以使⽤
缺点:
- 不适合写复杂的配置⽂件
⽐如properties格式如下
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/*
转换的过程也⽐较花费精⼒, 如果配置更复杂⼀点, 可读性会更差, 代码也会更难写
- 对格式有较强的要求(⼀个空格可能会引起⼀场⾎案)
练习
验证码案例
随着安全性的要求越来越⾼, ⽬前项⽬中很多都使⽤了验证码, 验证码的形式也是多种多样, 更复杂的图形验证码和⾏为验证码已经成为了更流⾏的趋势.
验证码的实现⽅式很多, ⽹上也有⽐较多的插件或者⼯具包可以使⽤, 咱们选择使⽤Google的开源项⽬Kaptcha来实现.
Kaptcha 插件介绍
Kaptcha 是Google的⼀个⾼度可配置的实⽤验证码⽣成⼯具. 代码:http://code.google.com/p/kaptcha/
⽹上有很多⼈甚⾄公司基于Google的kaptcha进⾏了⼆次开发. 我们选择⼀个直接适配SpringBoot的开源项⽬
https://github.com/oopsguy/kaptcha-spring-boot
由于作者的⽂档写的不是很全, 下⾯简单介绍下插件的使⽤
- 原理
验证码可以客⼾端⽣成, 也可以服务器⽣成. 对于普通的字符验证码, 后端通常分两部分.
⼀是⽣成验证码内容, 根据验证码内容和⼲扰项等, ⽣成图⽚, 返回给客⼾端
⼆是把验证码内容存储起来, 校验时取出来进⾏对⽐.
kaptcha插件选择把验证码存储在Session⾥
- 引⼊依赖
<dependency>
<groupId>com.oopsguy.kaptcha</groupId>
<artifactId>kaptcha-spring-boot-starter</artifactId>
<version>1.0.0-beta-2</version>
</dependency>
- ⽣成验证码
该插件提供了两种⽅式⽣成验证码
-
通过代码来⽣成(参考⽂档: https://github.com/oopsguy/kaptcha-springboot/blob/master/README_zh-CN.md, 不再介绍)
-
仅通过配置⽂件来⽣成验证码(推荐)
Kaptcha详细配置
配置项 | 配置说明 | 默认值 |
---|---|---|
kaptcha.border | 图⽚边框,合法值:yes , no | yes |
kaptcha.border.color | 边框颜⾊,合法值: r,g,b (and optional alpha) 或者 white,black,blue. | black |
kaptcha.image.width | 图⽚宽 | 200 |
kaptcha.image.height | 图⽚⾼ | 50 |
kaptcha.producer.impl | 图⽚实现类 | com.google.code.kaptcha.impl.DefaultKaptcha |
kaptcha.textproducer.impl | ⽂本实现类 | com.google.code.kaptcha.text.impl.DefaultTextCreator |
kaptcha.textproducer.char.string | ⽂本集合,验证码值从此集合中获取 | abcde2345678gfynmnpwx |
kaptcha.textproducer.char.length | 验证码⻓度 | 5 |
kaptcha.textproducer.font.names | 字体 | Arial, Courier |
kaptcha.textproducer.font.size | 字体大小 | 40px. |
kaptcha.textproducer.font.color | 字体颜⾊,合法值: r,g,b 或者white,black,blue. | black |
kaptcha.textproducer.char.space | ⽂字间隔 | 2 |
kaptcha.noise.impl | 干扰实现类 | com.google.code.kaptcha.impl.DefaultNoise |
kaptcha.noise.color | ⼲扰 颜⾊,合法值: r,g,b 或者white,black,blue. | black |
kaptcha.obscurificator.impl | 图⽚样式:⽔纹com.google.code.kaptcha.impl.WaterRipoke⻥眼com.google.code.kaptcha.impl.FishEyeGimpy阴影com.google.code.kaptcha.impl.ShadowGimpy | com.google.code.kaptcha.impl.WaterRipple |
kaptcha.background.impl | 背景实现类 | com.google.code.kaptcha.impl.DefaultBackground |
kaptcha.background.clear.from | 背景颜⾊渐变,开始颜⾊ | light grey |
kaptcha.background.clear.to | 背景颜⾊渐变, 结束颜⾊ | white |
kaptcha.word.impl | ⽂字渲染器 | com.google.code.kaptcha.text.impl.DefaultWordRenderer |
kaptcha.session.key | session key | KAPTCHA_SESSION_KEY |
kaptcha.session.date | session date | KAPTCHA_SESSION_DATE |
⽂档没有介绍, 感兴趣的同学翻看源码:
com.oopsguy.kaptcha.autoconfigure.KaptchaProperties
也可以使⽤ kaptcha.items
配置多个验证码⽣成器
kaptcha.items
是⼀个Map, key为验证码⽣成器名称, value为验证码⽣成器的配置
kaptcha:
items:
# home captcha
home:
path: /home/captcha
session:
key: HOME_KAPTCHA_SESSION_KEY
date: HOME_KAPTCHA_SESSION_DATE
# admin captcha
admin:
path: /admin/captcha
session:
key: ADMIN_KAPTCHA_SESSION_KEY
date: ADMIN_KAPTCHA_SESSION_DATE
配置说明:
配置后, 可以直接访问 http://XXXX:port/home/captcha 即可⽣成验证码
需求
界⾯如下图所⽰
- ⻚⾯⽣成验证码
- 输⼊验证码, 点击提交, 验证⽤⼾输⼊验证码是否正确, 正确则进⾏⻚⾯跳转
准备工作
创建项⽬, 引⼊SpringMVC的依赖包, 把前端⻚⾯放在项⽬中
约定前后端交互接口
需求分析
后端需要提供两个服务
- ⽣成验证码, 并返回验证码
- 校验验证码是否正确: 校验验证码是否正确.
接⼝定义
- ⽣成验证码
请求:
GET /admin/captcha
响应: 图⽚内容
浏览器给服务器发送⼀个
GET /admin/captcha
这样的请求, 服务器返回⼀个图⽚, 浏览器显⽰在⻚⾯上
- 校验验证码是否正确
请求: /admin/check
POST /admin/check
captcha=xn8d
captcha : ⽤⼾输⼊的验证码
响应:
true
根据⽤⼾输⼊的验证码, 校验验证码是否正确. true: 验证成功. false: 验证失败.
实现服务器代码
- 引⼊依赖
<dependency>
<groupId>com.oopsguy.kaptcha</groupId>
<artifactId>kaptcha-spring-boot-starter</artifactId>
<version>1.0.0-beta-2</version>
</dependency>
- 通过配置创建验证码⽣成器
kaptcha:
image:
width: 220
height: 160
textproducer:
font:
size: 40
items:
# home captcha
admin:
path: /admin/captcha
session:
key: HOME_KAPTCHA_SESSION_KEY
date: HOME_KAPTCHA_SESSION_DATE
启动项⽬, 访问 http://127.0.0.1:8080/admin/captcha , 显⽰验证码
- 验证码校验
@RequestMapping("/admin")
@RestController
public class CaptchaController {
//这个是从配置文件复制的
//企业标准:
//常量定义:key:全部大写,单词之间用下划线分隔 value:通常是小写,以下划线分隔,但这里特殊
private static final String KAPTCHA_SESSION_KEY = "HOME_KAPTCHA_SESSION_KEY";
private static final String KAPTCHA_SESSION_DATE = "HOME_KAPTCHA_SESSION_DATE";
//验证码的有效时间,这里是以毫秒为单位,上限是60s
private static final Long SESSION_TIMEOUT = 60 * 1000L;
//验证成功:true
//验证失败:false
/**
* 1.从session中获取到生成的验证码
* 2.比对前端传递的验证码和session中存储的是否一致
*
* @param captcha
* @param session
* @return
*/
@RequestMapping("/check")
public Boolean check(String captcha, HttpSession session) {
if (!StringUtils.hasLength(captcha)) {
return false;
}
//从session中获取验证码
String saveCaptcha = (String) session.getAttribute(KAPTCHA_SESSION_KEY);
Date saveData = (Date) session.getAttribute(KAPTCHA_SESSION_DATE);
//比对验证码
if (captcha.equals(saveCaptcha)) {
//比对日期
if (saveData == null || System.currentTimeMillis() - saveData.getTime() < SESSION_TIMEOUT) {
return true;
}
}
return false;
}
}
⽐对Session中存储的验证码是否和⽤⼾输⼊的⼀致
如果⼀致,并且时间在⼀分钟以为就认为成功
调整前端⻚⾯代码
修改 index.html
- 补充ajax代码, 点击提交按钮, 发送请求去服务端进⾏校验
$("#checkCaptcha").click(function () {
$.ajax({
type:"get",
url:"/admin/check",
data:{
captcha: $("#inputCaptcha").val()
},
success:function(result){
if(result){
location.href="success.html";
//location.assign("success.html");
}else{
alert("验证码错误");
}
}
});
});
运⾏测试
通过 URL http://127.0.0.1:8080/index.html 访问服务
总结
-
properties 是以 key=value 的形式配置的键值类型的配置⽂件, yml 使⽤的是树形配置⽅式.
-
读取配置⽂件内容, 使⽤
@Value
注解, 注解内使⽤"${}
"的格式读取. -
yml 层级之间使⽤换⾏缩进的⽅式配置, key 和 value 之间使⽤": "(英⽂冒号)加空格的⽅式设置, 并且空格不可省略.
-
properties 为早期并且默认的配置⽂件格式, 其配置存在⼀定的冗余数据, 使⽤ yml 可以很好的解决数据冗余的问题, 但不适合复杂配置.
-
yml 可以和 properties 共存,但⼀个项⽬中建议使⽤⼀种配置类型⽂件.