SpringBoot的配置文件相关学习
以下两个都是全局配置文件:
application.properties
application.yml或application.yaml
修改SpringBoot自动配置的默认值;底层会给一些自动配置,比如想要改端口的话,就得修改配置文件;
YAML YAML Ain't Markup Language概念:一种是又不是的标记化语言。很奇怪哎
标记语言:
以前的配置文件一半都是xxx.xml文件,YAML以数据为中心
yml修改端口号的方法见.yml文件,如下图:
properties修改端口号的方法,如下图:
YAML基本语法
基本语法:
(1)key : value (key空格,然后冒号:再写一个空格,再写值)
(2)以空格的缩进来控制层级关系,左缩进空格同宽的元素 都是同层级的配置
(3)属性和值都是大小写敏感的。
以下是demo:
server:
port : 8081
path : /hello
值的写法
(1)字面值:普通的值(数字,字符串,布尔值) 这些类型的表示方法:
key: value 字面值直接来写,字符串类型的值不用加单引号或双引号。
双引号“”和单引号’‘含义不同:带了双引号的字符串,本字符串不会被转义。但是单引号包装字符串的话特殊字符就依然是普通字符。比如一个name:“张三\n李四”,如果用双引号,则结果会会换行,而使用单引号会输输出:张三\n李四
(2)对象、Map;
当值类型是对象的时候,key : value 在下一行写属性和值的关系,但是要留意缩进:
比如:这个例子,friends是个对象,friends跟:之间会有一个空格,先严格按照以下配置拷贝去写
firends :
lastName: zhangsan
age: 20
另外一种定义对象 和属性值的行内写法格式:
friends2 : { lastName : zhangsan, age : 20}
(3)数组(List,Set)
第一种写法,用-值表示数组中的一个元素,或者[]普通定义法,以下是两种写法:
pets :
- cat
- dog
- pig
pats : [cat, dot, pig]
配置文件值的注入
如下是测试的yml配置文件,为了对象绑定值:
person :
lastName : 张三
age : 18
boss : false
birth : 2017/12/12
maps : { key1 : value 1, key2 : 2 }
list :
- 李四
- 王五
dog :
name : 小狗
age : 2
如下是待绑定的对象Person定义:
package com.example.userinitialzr.demo.bean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**将.yml配置的每一个属性值 映射到java对象属性中
@ConfigurationProperties 这个注解 把配置文件中的每个属性跟对象属性关联,prefix参数值确定 要映射配置文件中哪个配置中的值映射过来
只有这个组件是荣国旗中的组件才能提供容器中ConfigurationProperties的功能, @Component标识这是一个容器组件!
**/
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String lastName;
private Integer age;
private Boolean boss;
private Date birth;
private Map<String, Object> maps;
private List<Object> list;
private Dog dog;
@Override
public String toString() {
return "Person{" +
"lastName='" + lastName + '\'' +
", age=" + age +
", boss=" + boss +
", birth=" + birth +
", maps=" + maps +
", list=" + list +
", dog=" + dog +
'}';
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Boolean getBoss() {
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> getList() {
return list;
}
public void setList(List<Object> list) {
this.list = list;
}
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
}
里面的子对象Dog的定义:
package com.example.userinitialzr.demo.bean;
public class Dog {
private String name;
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
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;
}
}
最后可以自默认的测试目录的测试类去验证,项目启动时打印待绑定的对象的值,如下图:
如图是测试效果,其中IDEA的类和方法名的前面有个绿色的小箭头,右键提示可以直接运行该类或该方法,很方便,如下图:
YAML注释的关键字#,如下:
#person :
# lastName : 张三
以.properties方式配置person的值:
server.port=8081
# 配置person的值
person.lastName=张三
person.age=12
person.birth=2018/12/24
person.list=a,b,c
person.boss=false
person.maps.k1=v1
person.maps.k2=v2
person.dog.name=小狗
但需要注意的是,这个配置方式需要指定下文件的编码格式,指定文件运行时转为ASCII码才可以正常读取中文,如下图:
注意一下属性绑定的写法,以下是一些松散语法:
(1)标准写法 person.firstName
(2)大写用-替代,即 person.first-name
(3)大写用_替代,即 person.first_name
(4)当时系统属性的时候推荐这样写:PERSON_FIRST_NAME
具体针对数据校验的demo:当从配置文件读取时,如下demo演示了,读取properties的配置,使用@Email注解来验证数据是否是邮件类型的时候,直接获取person属性的值是会报错的,如下图:
通过@Value为属性赋值的三种方式:
@Component
@ConfigurationProperties(prefix = "person")
@Validated//表示要校验字段了
public class Person {
/*
bean对象的取值映射方法:第一种方法${}
*/
@Email //通过这个注解来校验 数值
@Value("${person.last-name}") //只能用这种写法,用person.lastName写法会报错,但是在.properties各种写法都支持
private String lastName;
/**
* 第二种取值方法 #{},大括号内传入一个表达式
*/
@Value("#{12*3}")
private Integer age;
/**
* 第三种取值方式,直接赋值
*/
@Value("true")
private Boolean boss;
注意:Java bean的对象配置值的方式比较(使用.properties方式和@Value的方式):
@ConfigurationProperties(就是写.properties文件或.yaml文件的方式) |
@Value(直接在对象的属性前面加注解) |
|
功能 |
批量注入配置文件中的属性 |
一个个指定属性的值 |
松散绑定(松散语法) |
支持 |
不支持 |
SpEL(Spring写法,如#{}表达式) |
不支持 |
支持 |
GSR303数据校验 |
支持 |
不支持 |
复杂类型封装 |
支持 |
不支持 |
但是相同点:
(1)两种方式(配置文件和 @Value的方式 )都可以获取到值
总结,两种配置方式该什么时候使用呢?
(1)如果只是在某个业务逻辑中获取一下配置文件中的某个值,可以使用@Value
(2)如果专门写了一个java bean(一个.java)专门来处理数据映射,那就应该使用.properties去配置最完备的数据
配置文件的一些关键字:
@PropertySource和@ImportResource
@PropertySource含义:加载指定的配置文件,因为@ConfigurationProperties的注解默认读取了全局的配置文件,如下图:
该关键字用法:
@Component
@PropertySource(value = {"classpath:application.properties"}) //加载指定的配置文件,使得该bean从该配置文件去绑定属性
@ConfigurationProperties(prefix = "person") //如果前面没有@PropertySource注解的话,读取的是全局的配置文件
//@Validated//表示要校验字段了
public class Person {
/*
bean对象的取值映射方法:第一种方法${}
*/
// @Email //通过这个注解来校验 数值
// @Value("${person.last-name}") //只能用这种写法,用person.lastName写法会报错,但是在.properties各种写法都支持
private String lastName;
@ImportProperties导入配置文件,让配置文件的内容生效
Spring Boot里没用的配置文件,默认是不生效的。把这个注解标注在配置类上,让配置文件生效
@ImportResource(locations = {"classpath:beans.xml"})
Sping Boot推荐给容器添加组件的方式:其中beans.xml是我自己写的配置文件,跟默认.properties同级文件夹(DemoApplication是我的主程序类)
package com.example.userinitialzr.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;
@ImportResource(locations = {"classpath:beans.xml"})//导入自己写的配置文件
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
其中beans.xml的内容:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--添加一个bean组件 ,配置bean的格式:id:bean的对象名,class:该类所在的包路径-->
<bean id="helloService2" class="com.example.userinitialzr.demo.service.HelloService"></bean>
</beans>
测试配置是否成功加载的测试类:
package com.example.userinitialzr.demo;
import com.example.userinitialzr.demo.bean.Person;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.junit4.SpringRunner;
/**
* 这是SpringBoot的单元测试类
*
* 可以在测试期间方便的,进行自动注入容器的功能
*/
@RunWith(SpringRunner.class) //使用Spring的驱动器去跑。什么是驱动器不知道
@SpringBootTest //标识这是一个单元测试类
public class DemoApplicationTests {
@Autowired
Person person;
@Autowired
ApplicationContext ioc;
@Test
public void contextLoads() {
// System.out.println("yanruTODO person="+person+"@"); //打印看看绑定完的person对象的值
}
//判断容器中有没有HelloService
@Test //添加了@Test这个注解,当运行该类的时候就会跑这个测试函数
public void testHelloService()
{
boolean b = ioc.containsBean("helloService2");
System.out.println("是否存在helloService?"+b);
}
}
Spring Boot推荐的配置文件方式:直接使用配置类来配置值
package com.example.userinitialzr.demo.config;
import com.example.userinitialzr.demo.service.HelloService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//指明当前类是一个配置类。
@Configuration
public class MyAppConfig {
//将方法的返回值添加到容器中,容器中的这个组件的默认id,就是函数的名称
@Bean
public HelloService helloServiceYanru()
{
System.out.println("给容器中添加组件");
return new HelloService();
}
}
配置文件中的占位符
随机数,如下${random.uuid}
占位符获取之前配置的值,如果没有,使用:冒号指定默认值,如下图:
Profile
功能:用来实现Spring Boot灵活选择生产环境还是测试环境
配主配置文件编写的时候,文件名可以是application-(profile).properties/.yml
默认调取的配置文件时application.properties文件
比如我创建了两个配置文件,区别在于使用的端口不同,如下图:
在主配置文件中使用如下代码去指定使用哪个配置文件:
(1)第一种方式:server.profiles.active=dev(在我本地一配置这个 启动就报错了)
server.port=8081
##指定使用哪一个 配置文件
#server.profiles.active=dev
(2)第二种方式,命令行方式:
在项目启动项属性设置 指定系统参数:--spring.profiles.active=dev,如下图:
(3)jar包运行命令行的方式:
java -jar XXX.jar --spring.profiles.active=dev
(4)虚拟机参数:
-Dspring.profiles.active=dev
如下图:
Spring Boot配置文件加载设置
配置文件的存放位置:
(1)file:.config/
(2)file:./
(3)classpath:/config/
(4)classpath:/
以上按照优先级从高到低的顺序,所有的配置文件都会被加载,高优先级的配置文件覆盖低优先级的
使用spring.config.location改变配置文件的位置
项目打包以后,使用命令行参数的形式,“热更新”配置来实现对服务器的部分修改:
#指定配置文件的位置
spring.config.location=/application.properties
此外,除了项目内的资源,Spring Boot还可以加载外部的配置文件,可以详细的查看官方说明。
此外,当多个配置文件同时存在被加载的时候,形成互补配置的状态,冲突的部分会由高优先级的覆盖低优先级的配置文件;
当同时存在带profile的和不带profile的配置文件,会优先加载带Profile的配置。
命令行启动项目时指定参数:
java -jar D:\MyProjects\SpringBootStudyFromProginn2\demoForConfigLoad\target\demo-0.0.1-SNAPSHOT.jar --spring.context.path=/abc -- server.port=8087
自动配置的原理
配置文件能配置的属性,参见Spring Boot官网。
自动配置原理
(1)Spring Boot启动的时候加载主配置类,开启了自动配置功能@EnableAutoConfiguration
(2)@EnableAutoConfiguration的作用:
利用EnableAutoConfigurationImportSelector给容器中导入一些组件
可以查看selectImports()方法的内容
获取候选的配置方法:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
一些重要的函数说明:
List<String> configurations = SpringFactoriesLoader.loadFactoryNames()
将类路径下META-INF/spring.factories里面配置的所有的EnableAutoConfiguration的值加入到了容器中。
每一个XXXAutoConfiguration类都是容器中的一个组件,都加入到容器中,通过他们来实现自动配置
(3)每一个自动配置类,进行自动配置功能;
(4)以HttpEncodingAutoConfiguration为例解释自动配置的原理:
@Configuration //表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件
@EnableConfigurationProperties(HttpEncodingProperties.class);//启动指定类的
//ConfigurationProperties功能:
@ConditionalOnWebApplication //作用是根据不同的条件,如果满足指定的条件,整个配置类才会生效,这个注解是判断当前应用是否Web应用,是的话,配置生效
@ConditionalOnWClass(CharacterEncodingFilter.class); //判断当前的项目有没有这个类,CharacterEncodingFilter是乱码的过滤器
@ConditionalOnProperty(prefix="string.http.encoding", value="enabled" matchIfMissing=true) //判断配置文件是否存在某个配置,string.http.encoding.enabled这个属性值,matchIfMissing=true表示,即使没有.enabled这个属性,这个配置也生效
public class HttpEncodingAutoConfiguration{
private final HttpEncodingProperties properties;
//只有一个有参构造器的情况下,参数的值就会从容器中拿
public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
this.properties = properties;
}
@Bean //给容器中添加一个组件,这个组件的某些值需要从properties中获取
@ConditionalOnMissingBean({CharacterEncodingFilter.class})
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
return filter;
}
(5)所有在配置文件中的配置的属性都是在XXXProperties类中封装者,配置文件能配什么就可以参照某个功能对应的这个属性类
一旦这个配置类生效,这个配置类就会向容器中添加各种各样的组件;
Spring Boot的精髓:
(1)Sprinig Boot启动会加载大类的自动配置类;
(2)看我们需要的功能有没有Spring Boot默认写好的自动配置类
(3)我们再来看这个自动配置中到底配置了哪些组件:只要我们要用的组件有,就不再需要配置了
(4)给容器中自动配置类添加组件的时候,会从propertie类中获取某些组件,我们就可以在配置文件中指定这些属性的值;
XXXAutoConfiguration自动配置类:给容器中添加组件;
xxxProperties中封装配置类所需要的属性
@Conditional派生注解(Spring 注解版原生的@Conditional作用)
作用:必须是@Conditional指定的条件成立,才给容器中添加组件,否则该组件不生效
举例:
@ConditionalOnJava:Java的版本是否符合要求
@ConditionOnMissingBean:判断容器中是否没有某个组件,没有的话,该条件成立
@ConditionalOnProperty:配置中是否配置了某个属性
等等
Q:我们怎么知道哪些自动配置类生效了?
在.properties属性配置中指定:
debug=true
项目启动时,控制台会打印自动配置类的debug信息,就方便知道到底哪些自动配置类生效了
log中的关键字:
Positive matches:(自动配置类启用的)
Negative matches:(没有成功启用的自动配置类)