【JavaEE】深入浅出:Spring Boot配置文件全解析

目录


SpringBoot 配置⽂件

  1. 学习SpringBoot配置⽂件的格式以及对应的语法
  2. 了解两个配置⽂件格式的差异

配置⽂件作⽤

热加载:写的代码,立即就被加载进来,生效了

热部署:如果代码发生了变动,就会重新部署

计算机上有数以千计的配置⽂件, 我们使⽤的绝⼤多数软件, ⽐如浏览器, 微信, Idea, 甚⾄电脑, ⼿机, 都离不开配置⽂件. 我们可能永远不会直接与其中的⼤部分⽂件打交道,但它们确实以不同的形式散落在我们的计算机上, ⽐如 C:\Users, C:\Windows⽂件夹, 以及各种 *.config, *.xml ⽂件

配置⽂件主要是为了解决硬编码带来的问题, 把可能会发⽣改变的信息, 放在⼀个集中的地⽅, 当我们启动某个程序时, 应⽤程序从配置⽂件中读取数据, 并加载运⾏. (简单一句话:解决硬编码的问题

硬编码是将数据直接嵌⼊到程序或其他可执⾏对象的源代码中, 也就是我们常说的"代码写死"。

⽐如⼿机字体⼤⼩

如果采⽤硬编码的⽅式, 就直接在程序中指定字体⼤⼩, 所有的⽤⼾使⽤的都是同⼀个字体⼤⼩,但是不同的⽤⼾有不同的偏好, 我们可以把⼿机字体的⼤⼩放在配置⽂件中, 当程序启动时, 读取配置, 以⽤⼾设置的字体⼤⼩来显⽰

使⽤配置⽂件, 可以使程序完成⽤⼾和应⽤程序的交互, 或者应⽤程序与其他应⽤程序的交互

SpringBoot配置⽂件

SpringBoot的配置文件只有application后缀可以是.yml或者是.properties

不过这个名字可以改,但没必要改

SpringBoot⽀持并定义了配置⽂件的格式, 也在另⼀个层⾯达到了规范其他框架集成到SpringBoot的⽬的.

很多项⽬或者框架的配置信息也放在配置⽂件中, ⽐如:

  • 项⽬的启动端⼝
  • 数据库的连接信息(包含⽤⼾名和密码的设置)
  • 第三⽅系统的调⽤密钥等信息
  • ⽤于发现和定位问题的普通⽇志和异常⽇志等

项⽬的启动端⼝

SpringBoot内置了Tomcat服务器, 默认端⼝号是8080, 但是⽤⼾电脑上8080端⼝号有可能就被其他应⽤程序占⽤了, 所以SpringBoot需要⽀持让⽤⼾⾃定义端⼝号数据库连接信息

为了更⽅便简单的访问数据库, 出现了⼀些持久层框架, 其实就是对JDBC进⾏了更深层次的封装.让⽤⼾通过简单⼏⾏代码就可完成数据库的访问. 但是不同的应⽤程序访问的数据库不同, 这些持久层框架就需要⽀持⽤⼾可以⾃定义配置数据库的连接信息.

运行SpringBoot程序

  1. 打jar包
  1. 通过 java -jarXXX.jar

配置⽂件快速⼊⼿

我们在前⾯讲了Tocmat 默认端⼝号是8080, 所以我们程序访问时的端⼝号也是8080

但是如果8080端⼝号已经被其他进程使⽤了呢? 我们可以通过配置⽂件来修改服务的端⼝号

SpringBoot 在创建项⽬时, 就已经帮我们创建了配置⽂件

修改 application.properties ⽂件

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.propertiesapplication.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 属于新版包装,如果⽤⼾了解情况直接指定要新款包装,那么就直接发给他

特殊说明

  1. 理论上讲 .properties.yml 可以并存在于⼀个项⽬中,当 .properties.yml并存时,两个配置都会加载 . 如果配置⽂件内容有冲突 , 则以 .properties 为主, 也就是 .properties 优先级更⾼ 。并不是.yml会不生效

  2. 虽然理论上来讲 .properties 可以和 .yml 共存,但实际的业务当中,我们通常会采取⼀种统⼀的配置⽂件格式(也就是只用其中一个文件就行) ,这样可以更好的维护(降低故障率)。现在都是用.yml更多

properties 配置⽂件说明

properties 配置⽂件是最早期的配置⽂件格式,也是创建 SpringBoot 项⽬默认的配置⽂件

properties 基本语法

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

properties 复制代码
# 配置项⽬端⼝号
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 配置如下:

properties 复制代码
# 自定义配置
demo.key1=hello.properties
java 复制代码
@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 使⽤⽰例:

yaml 复制代码
# 修改端口号
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
yaml 复制代码
# 字符串
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配置:

yaml 复制代码
demo:
  key1: hello,yml
java 复制代码
@RestController
public class YmlController {
    @Value("${demo.key1}")
    public String key1;

    @RequestMapping("/readYml")
    public String readYml(){
        return key1;
    }
}

访问: http://127.0.0.1:8080/readYml

运⾏结果:

yaml 复制代码
demo:
  key1: hello,yml
  key2: hello,key2
  key3: hello,key3

这里用了这个**@PostConstruct注解,修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次**

java 复制代码
@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 中配置如下信息:

yaml 复制代码
string:
	str1: Hello \n Spring Boot.
	str2: 'Hello \n Spring Boot.'
    str3: "Hello \n Spring Boot."

读取程序实现代码如下:

java 复制代码
@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 表⽰的是它本⾝的含义, 就是换⾏

配置对象
  1. 配置
  2. 配置内容读取
  3. 注入

我们还可以在 yml 中配置对象,如下配置:

  1. 配置
yaml 复制代码
Student:
  id: 18
  name: zhangsan
  age: 12

或者是使⽤⾏内写法(与上⾯的写法作⽤⼀致):但可读性差

yaml 复制代码
Student: {id: 18,name: zhangsan,age: 12}

这个时候就不能⽤ @Value 来读取配置中的对象了,此时要使⽤另⼀个注解@ConfigurationProperties 来读取,具体实现如下:

  1. 配置内容读取
java 复制代码
@Component
@ConfigurationProperties(prefix = "student")
@Data
public class Student {
    private Integer id;
    private String name;
    private Integer age;
}

调⽤类的实现如下:

  1. 注入
java 复制代码
	@Autowired
    public Student student;
	@PostConstruct
    public void init(){
        System.out.println("student:"+student);
    }

运⾏结果如下:

配置集合
  1. 配置
  2. 配置内容读取
  3. 注入

配置⽂件也可以配置 list 集合,如下所⽰:

  1. 配置
yaml 复制代码
dbtypes:
  name:
    - mysql
    - sqlserver
    - db2

记得一定要加空格,如果不加,这部分内容就会变成一个整体,统一赋值给对应属性

集合的读取和对象⼀样,也是使⽤ @ConfigurationProperties 来读取的,具体实现如下:

  1. 配置内容读取

注意这里的name要和配置里的name要对应上,dbtypes也要对应上

java 复制代码
@Component
@ConfigurationProperties(prefix = "dbtypes")
@Data
public class DBType {
    private List<String> name;
}

访问集合的实现如下:

  1. 注入
java 复制代码
	@Autowired
    public DBType dbType;
	@PostConstruct
    public void init(){
        System.out.println("dbtype:"+dbType);
    }

运⾏结果如下:

配置Map
  1. 配置
  2. 配置内容读取
  3. 注入

配置⽂件也可以配置 map,如下所⽰:

  1. 配置
yaml 复制代码
dbtypes:
  name:
    - mysql
    - sqlserver
    - db2
  map:
    k1: kk1
    k2: kk2
    k3: kk3

Map的读取和对象⼀样,也是使**⽤ @ConfigurationProperties 来读取**的,具体实现如下:

  1. 配置内容读取

注意这里的map要和配置里的map要对应上,dbtypes也要对应上

java 复制代码
@Component
@ConfigurationProperties(prefix = "dbtypes")
@Data
public class DBType {
    private List<String> name;

    private HashMap<String,String> map;
}

打印类的实现如下:

  1. 注入
java 复制代码
	@Autowired
    public DBType dbType;
	@PostConstruct
    public void init(){
        System.out.println("dbtype:"+dbType);
    }

运⾏结果如下:

yml优缺点

优点:

  1. 可读性⾼,写法简单, 易于理解
  2. ⽀持更多的数据类型 , 可以简单表达对象, 数组, List,Map等数据形态.
  3. ⽀持更多的编程语⾔, 不⽌是Java中可以使⽤, 在Golang, Python, Ruby, JavaScript中也可以使⽤

缺点:

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

⽐如properties格式如下

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

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

转换的过程也⽐较花费精⼒, 如果配置更复杂⼀点, 可读性会更差, 代码也会更难写

  1. 对格式有较强的要求(⼀个空格可能会引起⼀场⾎案)

练习

验证码案例

随着安全性的要求越来越⾼, ⽬前项⽬中很多都使⽤了验证码, 验证码的形式也是多种多样, 更复杂的图形验证码和⾏为验证码已经成为了更流⾏的趋势.

验证码的实现⽅式很多, ⽹上也有⽐较多的插件或者⼯具包可以使⽤, 咱们选择使⽤Google的开源项⽬Kaptcha来实现.

Kaptcha 插件介绍

Kaptcha 是Google的⼀个⾼度可配置的实⽤验证码⽣成⼯具. 代码:http://code.google.com/p/kaptcha/

⽹上有很多⼈甚⾄公司基于Google的kaptcha进⾏了⼆次开发. 我们选择⼀个直接适配SpringBoot的开源项⽬

https://github.com/oopsguy/kaptcha-spring-boot

由于作者的⽂档写的不是很全, 下⾯简单介绍下插件的使⽤

  1. 原理

验证码可以客⼾端⽣成, 也可以服务器⽣成 . 对于普通的字符验证码, 后端通常分两部分.

⼀是**⽣成验证码内容,** 根据验证码内容和⼲扰项等, ⽣成图⽚, 返回给客⼾端

⼆是把验证码内容存储起来, 校验时取出来进⾏对⽐.

kaptcha插件选择把验证码存储在Session

  1. 引⼊依赖
xml 复制代码
<dependency>
	<groupId>com.oopsguy.kaptcha</groupId>
	<artifactId>kaptcha-spring-boot-starter</artifactId>
	<version>1.0.0-beta-2</version>
</dependency>
  1. ⽣成验证码

插件提供了两种⽅式⽣成验证码

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为验证码⽣成器的配置

yaml 复制代码
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 即可⽣成验证码

需求

界⾯如下图所⽰

  1. ⻚⾯⽣成验证码
  2. 输⼊验证码, 点击提交, 验证⽤⼾输⼊验证码是否正确, 正确则进⾏⻚⾯跳转
准备工作

创建项⽬, 引⼊SpringMVC的依赖包, 把前端⻚⾯放在项⽬中

约定前后端交互接口

需求分析

后端需要提供两个服务

  1. ⽣成验证码, 并返回验证码
  2. 校验验证码是否正确: 校验验证码是否正确.

接⼝定义

  1. ⽣成验证码

请求:

GET /admin/captcha

响应: 图⽚内容

浏览器给服务器发送⼀个 GET /admin/captcha 这样的请求, 服务器返回⼀个图⽚, 浏览器显⽰在⻚⾯上

  1. 校验验证码是否正确

请求: /admin/check

POST /admin/check

captcha=xn8d

captcha : ⽤⼾输⼊的验证码

响应:

true

根据⽤⼾输⼊的验证码, 校验验证码是否正确. true: 验证成功. false: 验证失败.

实现服务器代码
  1. 引⼊依赖
xml 复制代码
<dependency>
	<groupId>com.oopsguy.kaptcha</groupId>
	<artifactId>kaptcha-spring-boot-starter</artifactId>
	<version>1.0.0-beta-2</version>
</dependency>
  1. 通过配置创建验证码⽣成器
yaml 复制代码
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 , 显⽰验证码

  1. 验证码校验
java 复制代码
@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

  1. 补充ajax代码, 点击提交按钮, 发送请求去服务端进⾏校验
js 复制代码
$("#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 访问服务

总结

  1. properties 是以 key=value 的形式配置的键值类型的配置⽂件, yml 使⽤的是树形配置⽅式.

  2. 读取配置⽂件内容, 使⽤ @Value 注解 , 注解内使⽤" ${} "的格式读取.

  3. yml 层级之间使⽤换⾏缩进的⽅式配置, key 和 value 之间使⽤": "(英⽂冒号)加空格的⽅式设置, 并且空格不可省略.

  4. properties 为早期并且默认的配置⽂件格式, 其配置存在⼀定的冗余数据, 使⽤ yml 可以很好的解决数据冗余的问题, 但不适合复杂配置.

  5. yml 可以和 properties 共存,但⼀个项⽬中建议使⽤⼀种配置类型⽂件.

相关推荐
morris1312 分钟前
【SpringBoot】Xss的常见攻击方式与防御手段
java·spring boot·xss·csp
七星静香27 分钟前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
Jacob程序员28 分钟前
java导出word文件(手绘)
java·开发语言·word
ZHOUPUYU28 分钟前
IntelliJ IDEA超详细下载安装教程(附安装包)
java·ide·intellij-idea
stewie632 分钟前
在IDEA中使用Git
java·git
Elaine2023911 小时前
06 网络编程基础
java·网络
G丶AEOM1 小时前
分布式——BASE理论
java·分布式·八股
落落鱼20131 小时前
tp接口 入口文件 500 错误原因
java·开发语言
想要打 Acm 的小周同学呀1 小时前
LRU缓存算法
java·算法·缓存
镰刀出海1 小时前
Recyclerview缓存原理
java·开发语言·缓存·recyclerview·android面试