配置文件
SpringBoot使用一个全局的配置文件,配置文件名application
是固定的;
application.properties
application.yml
application.yaml
配置文件的目录:
SpringBoot启动会扫描以下位置的application.properties
或者application.yml
文件作为Springboot的默认配置文件
file: ./config/
file: ./
classpath:/config/
classpath:/
优先级由高到低,高优先级的配置会覆盖低优先级的配置。SpringBoot会从这四个位置全部加载主配置文件;互补配置;
这里项目根路径下的配置文件maven编译时不会打包过去,需要修改pom
另外,可以通过运行打包文件时,使用命令行参数的形式,启动项目对的时候来指定配置文件的配置。
java -jar xxx.jar --spring.config.location=/home/cloudlandboy/application.yaml
配置文件语法
yaml
YAML isn’t Markup Language:不是一个标记语言;以前的配置文件;大多都使用的是 xxxx.xml文件;YAML:以数据为中心,比json、xml等更适合做配置文件;
以空格
的缩进来控制层级关系;只要是左对齐的一列数据,都是同一个层级的次等级的前面是空格,不能使用制表符(tab)冒号之后如果有值,那么冒号和值之间至少有一个空格,不能紧贴着
字面量:普通的值(数字,字符串,布尔)
k: v
字符串默认不用加上单引号或者双引号;
""
:双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思
eg: name: “zhangsan \n lisi”:输出;zhangsan 换行 lisi
''
:单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据
eg: name: ‘zhangsan \n lisi’:输出;zhangsan \n lisi
对象、Map(属性和值)
k: v
在下一行来写对象的属性和值的关系;注意缩进
-
person: name: 张三 gender: 男 age: 22Copy to clipboardErrorCopied
-
行内写法
person: {name: 张三,gender: 男,age: 22}Copy to clipboardErrorCopied
数组(List、Set)
-
fruits: - 苹果 - 桃子 - 香蕉Copy to clipboardErrorCopied
-
行内写法
fruits: [苹果,桃子,香蕉]
配置文件值注入
public class Pet {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Pets{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
/**
* @Author cloudlandboy
* @Date 2019/11/13 下午8:06
* @Since 1.0.0
* 将配置文件中配置的每一个属性的值,映射到这个组件中
*
* ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定;
* prefix = "person":配置文件中哪个下面的所有属性进行一一映射
* 只有这个组件是容器中的组件,才能容器提供的@ConfigurationProperties功能;
*/
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String name;
private Character gender;
private Integer age;
private boolean boss;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Pet pet;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Character getGender() {
return gender;
}
public void setGender(Character gender) {
this.gender = gender;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public boolean isBoss() {
return boss;
}
public void setBoss(boolean boss) {
this.boss = boss;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
public Map<String, Object> getMaps() {
return maps;
}
public void setMaps(Map<String, Object> maps) {
this.maps = maps;
}
public List<Object> getLists() {
return lists;
}
public void setLists(List<Object> lists) {
this.lists = lists;
}
public Pet getPet() {
return pet;
}
public void setPet(Pet pet) {
this.pet = pet;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", gender=" + gender +
", age=" + age +
", boss=" + boss +
", birth=" + birth +
", maps=" + maps +
", lists=" + lists +
", pet=" + pet +
'}';
}
}
<!--导入配置文件处理器,配置文件进行绑定就会有提示-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>Copy to clipboardErrorCopied
配置文件:
person:
name: 张三
gender: 男
age: 36
boss: true
birth: 1982/10/1
maps: {k1: v1,k2: v2}
lists:
- apple
- peach
- banana
pet:
name: 小狗
age: 12
Copy to clipboardErrorCopied
测试
package cn.clboy.helloworldquickstart;
import cn.clboy.helloworldquickstart.model.Person;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class HelloworldquickstartApplicationTests {
@Autowired
private Person person;
@Test
void contextLoads() {
System.out.println(person);
}
}
properties
上面yaml对应的properties配置文件写法
person.name=李四
person.age=34
person.birth=1986/09/12
person.boss=true
person.gender=女
person.lists=cat,dog,pig
person.maps.k1=v1
person.maps.k2=v2
person.pet.name="小黑"
person.pet.age=10
中文乱码解决办法
若yaml和properties配置文件同时存在,properties配置文件的内容会覆盖yaml配置文件的内容
配置文件值注入两种方法对比
配置文件值注入有两种方式,一个是Spring Boot的@ConfigurationProperties
注解,另一个是spring原先的@value
注解
@ConfigurationProperties | @Value | |
---|---|---|
功能 | 批量注入配置文件中的属性 | 一个个指定 |
松散绑定(松散语法) | 支持 | 不支持 |
SpEL | 不支持 | 支持 |
JSR303数据校验 | 支持 | 不支持 |
复杂类型封装 | 支持 | 不支持 |
松散绑定:例如Person中有lastName
属性,在配置文件中可以写成
lastName
或lastname
或last-name
或last_name
等等
@ImportResource
@ImportResource
注解用于导入Spring的配置文件,让配置文件里面的内容生效;(就是以前写的springmvc.xml、applicationContext.xml)
SpringBoot里面没有Spring配置文件,我们自己编写的配置文件,也不能自动识别;
想让Spring的配置文件生效,加载进来;@ImportResource标注在一个配置类上
@Configuration
SpringBoot推荐给容器中添加组件的方式;推荐使用全注解的方式。配置类**@Configuration** —equals—> Spring配置文件
@Bean
使用**@Bean**给容器中添加组件
package cn.clboy.helloworldquickstart.config;
import cn.clboy.helloworldquickstart.model.Pet;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Author cloudlandboy
* @Date 2019/11/14 下午4:33
* @Since 1.0.0
*
* Configuration:指明当前类是一个配置类;就是来替代之前的Spring配置文件
*/
@Configuration
public class BeanConfiguration {
/**
*相当于在配置文件中用<bean><bean/>标签添加组件
* Bean的属性可以在这里调用set或构造器方法载入,
* 也可以使用上述@Value和@ConfigurationProperties两种方法载入
*/
@Bean
public Pet myPet() {
Pet pet = new Pet();
pet.setName("嘟嘟");
pet.setAge(3);
return pet;
}
}
Profile
Profile是Spring对不同环境提供不同配置功能的支持,可以通过激活、指定参数等方式快速切换环境
多profile文件形式
文件名格式:application-{profile}.properties/yml,例如:
- application-dev.properties
- application-prod.properties
程序启动时会默认加载application.properties
,启动的端口就是8080
可以在主配置文件中指定激活哪个配置文件
yml支持多文档块方式
每个文档块使用---
分割
server:
port: 8080
spring:
profiles:
active: prod
---
server:
port: 8081
spring:
profiles: dev
---
server:
port: 8082
spring:
profiles: prod
激活指定profile的三种方式
-
在配置文件中指定 spring.profiles.active=dev(如上)
-
项目打包后在命令行启动
java -jar xxx.jar --spring.profiles.active=dev
-
虚拟机参数
-Dspring.profiles.active=dev
外部配置加载顺序
SpringBoot也可以从以下位置加载配置; 优先级从高到低;高优先级的配置覆盖低优先级的配置,所有的配置会形成互补配置
-
命令行参数
所有的配置都可以在命令行上进行指定
java -jar xxx.jar --server.port=8087 --server.context-path=/abcCopy to clipboardErrorCopied
多个配置用空格分开; --配置项=值
-
来自java:comp/env的JNDI属性
-
Java系统属性(System.getProperties())
-
操作系统环境变量
-
RandomValuePropertySource配置的random.*属性值
由jar包外向jar包内进行寻找;
优先加载带profile
- jar包外部的
application-{profile}.properties
或application.yml
(带spring.profile)配置文件 - jar包内部的
application-{profile}.properties
或application.yml
(带spring.profile)配置文件
再来加载不带profile
-
jar包外部的
application.properties
或application.yml
(不带spring.profile)配置文件 -
jar包内部的
application.properties
或application.yml
(不带spring.profile)配置文件 -
@Configuration注解类上的@PropertySource
-
通过SpringApplication.setDefaultProperties指定的默认属性
自动加载配置文件的原理
SpringBoot启动的时候加载主配置类,开启了自动配置功能
@SpringBootApplication
@EnableAutoConfiguration
@EnableAutoConfiguration的作用
-
利用
@Import(AutoConfigurationImportSelector.class)
给容器导入一些组件在
AutoConfigurationImportSelector
类中的selectImport
方法有下面方法
点进去
点进去,会有一个loadFactoryName
方法
点进去,会有loadSpringFactories
方法
该方法中有变量FACTORIES_RESOURCE_LOCATION
- 每一个这样的
xxxAutoConfiguration
类中都是容器中的一个组件,都加入到容器中,用他们来做自动配置; - 每一个自动配置类进行自动配置功能。
以HttpEncodingAutoConfiguration为例解释自动配置原理
package org.springframework.boot.autoconfigure.web.servlet;
......
//表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件
@Configuration(
proxyBeanMethods = false
)
/**
* 启动指定类的ConfigurationProperties功能;
* 将配置文件中对应的值和HttpProperties绑定起来;
* 并把HttpProperties加入到ioc容器中
*/
@EnableConfigurationProperties({HttpProperties.class})
/**
* Spring底层@Conditional注解
* 根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效;
* 判断当前应用是否是web应用,如果是,当前配置类生效
*/
@ConditionalOnWebApplication(
type = Type.SERVLET
)
//判断当前项目有没有这个类
@ConditionalOnClass({CharacterEncodingFilter.class})
/**
* 判断配置文件中是否存在某个配置 spring.http.encoding.enabled;如果不存在,判断也是成立的
* 即使我们配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的;
*/
@ConditionalOnProperty(
prefix = "spring.http.encoding",
value = {"enabled"},
matchIfMissing = true
)
public class HttpEncodingAutoConfiguration {
//它已经和SpringBoot的配置文件映射了
private final Encoding properties;
//只有一个有参构造器的情况下,参数的值就会从容器中拿
public HttpEncodingAutoConfiguration(HttpProperties properties) {
this.properties = properties.getEncoding();
}
@Bean //给容器中添加一个组件,这个组件的某些值需要从properties中获取
@ConditionalOnMissingBean //判断容器有没有这个组件?(容器中没有才会添加这个组件)
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
return filter;
}
......
- 根据不同的条件去判断,决定这个配置类是否生效
- 一旦这个配置类生效,这个配置类就会给容器中添加各种组件;这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;
所有在配置文件中能配置的属性都是在xxxxProperties
类中封装着;配置文件能配置什么就可以参照某个功能对应的这个属性类
总结
- SpringBoot启动会加载大量的自动配置类
- 我们看我们需要的功能有没有SpringBoot默认写好的自动配置类
- 再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件有,我们就不需要再来配置了)
- 给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们就可以在配置文件中指定这些属性的值
xxxxAutoConfigurartion
:自动配置类;
xxxxProperties
:封装配置文件中相关属性;
@Conditional派生注解
作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;
@Conditional扩展注解 | 作用(判断是否满足当前指定条件) |
---|---|
@ConditionalOnJava | 系统的java版本是否符合要求 |
@ConditionalOnBean | 容器中存在指定Bean; |
@ConditionalOnMissingBean | 容器中不存在指定Bean; |
@ConditionalOnExpression | 满足SpEL表达式指定 |
@ConditionalOnClass | 系统中有指定的类 |
@ConditionalOnMissingClass | 系统中没有指定的类 |
@ConditionalOnSingleCandidate | 容器中只有一个指定的Bean,或者这个Bean是首选Bean |
@ConditionalOnProperty | 系统中指定的属性是否有指定的值 |
@ConditionalOnResource | 类路径下是否存在指定资源文件 |
@ConditionalOnWebApplication | 当前是web环境 |
@ConditionalOnNotWebApplication | 当前不是web环境 |
@ConditionalOnJndi | JNDI存在指定项 |