配置文件解析(下)
原创者:文思
一、yml(YAML Ain’t Markup Language)基本用法
application.properties或application.yml
配置文件的作用:修改springboot的默认配置。
YAML:以数据为中心,比json,xml等更适合做配置文件
基本语法:
K:(空格)V :表示一对键值对。以空格的缩进来控制层级关系,只要是左对齐的一列数据,都是一个层级的。
值的写法:
字面量:普通的值(数据、字符串、布尔),k: v形式直接来写。如果有特殊字符,不需要转义可用双引号,需要转义可用单引号。
对象、Map:k: v形式直接来写,在下一行写对象的属性和值,注意缩进。
还可以使用行内写法:friends:{name:zhangsan,age: 18}
数组(List、Set):用- 值表示数组中的一个元素
示例:
新建一个yml配置文件:
server:
port: 8081
person:
lastName: zhangsan
age: 18
boss: false
birth: 2018/7/17
map: {t1:test1,t2:test2,t3:test3}
map2:
t1: test11
t2: test22
t3: test33
list:
- lisi
- wangwu
- zhaoliu
dog:
name: 小狗
age: 2
创建对应的对象:
@Component//声明为spring组件,让spring容器管理
@ConfigurationProperties(prefix="person")//默认从全局配置文件中获取值,告诉springboot将此类的属性与配置文件的配置绑定
public classPerson {
private String lastName;
private Integer age;
private Boolean boss;
private Date birth;
privateMap map;
privateMap map2;
private List list;
private Dog dog;
public String toString(){
return "Person{"+
"lastName="+ lastName+
",age="+ age+
",boss="+ boss+
",birth="+ birth+
",map="+ map+
",map2="+ map2+
",list="+ list+
",dog="+ dog+
"}";
}
.....setter\getter方法略
}
package com.wensi.springbootdemo1.yml;
public classDog {
private String name;
private Integer age;
public String getName() {
return name;
}
.....setter\getter方法略
}
运行测试类:
package com.wensi.springbootdemo1;
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.test.context.junit4.SpringRunner;
import com.wensi.springbootdemo1.yml.Person;
@RunWith(SpringRunner.class)
@SpringBootTest
public classAppTest{
@Autowired
Personperson;
@Test
public void contextLoads(){
System.out.println(person);
}
}
运行结果:
Person{lastName=zhangsan,age=18,boss=false,birth=Tue Jul 17 00:00:00 CST 2018,map={t1=test1, t2=test2, t3=test3},map2={t1=test11,t2=test22, t3=test33},list=[lisi, wangwu,zhaoliu],dog=com.wensi.springbootdemo1.yml.Dog@796d3c9f}
使用properties格式的配置文件application.properties:
person.lastName=zhangsan
person.age=18
person.boss=false
person.birth=2018/7/17
person.map.t1=test1
person.map.t2=test2
person.map.t3=test3
person.list=lisi,wangwu,zhaoliu
person.dog.name=小狗
person.dog.age=2
运行测试类的结果:
Person{lastName=zhangsan,age=18,boss=false,birth=TueJul 17 00:00:00 CST 2018,map={t3=test3, t2=test2,t1=test1},map2=null,list=[lisi, wangwu,zhaoliu],dog=com.wensi.springbootdemo1.yml.Dog@6f8d7714}
使用@value:
@Component
public class PersonValue {
@Value("${person.lastName}")
private String lastName;
@Value("#{2*11}")
private Integer age;
@Value("true")
privateBooleanboss;
运行结果:
Person{lastName=zhangsan,age=22,boss=true,birth=null,map=null,map2=null,list=null,dog=null}
@value和@ConfigurationProperties区别:
例如:
@Component
@ConfigurationProperties(prefix="person")
@Validated
public class Person {
private String lastName;//lastName必须是邮箱格式
private Integer age;
private Boolean boss;
private Date birth;
运行结果:
Binding to targetPerson{lastName=zhangsan,age=18,boss=false,birth=Tue Jul 17 00:00:00 CST2018,map={t3=test3, t2=test2, t1=test1},map2=null,list=[lisi, wangwu,zhaoliu],dog=com.wensi.springbootdemo1.yml.Dog@7689ddef} failed:
Property:person.lastName
Value:zhangsan
Reason: 不是一个合法的电子邮件地址
使用@value时:
@Component
@Validated
public class PersonValue {
@Value("${person.lastName}")
private String lastName;
运行结果:
Person{lastName=zhangsan,age=22,boss=true,birth=null,map=null,map2=null,list=null,dog=null}。说明@value不支持数据校验
对于复杂数据类型:
@Component
public class PersonValue {
@Value("${person.lastName}")
private String lastName;
@Value("#{2*11}")
private Integer age;
@Value("true")
private Boolean boss;
private Date birth;
@Value("${person.map}")
private Map map;
运行结果:
Caused by: java.lang.IllegalArgumentException:Could not resolve placeholder 'person.map' in value "${person.map}"
说明@value不支持复杂数据类型,如map。
使用建议:
如果说,我们只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用@Value。
如果说,我们专门编写了一个javaBean来和配置文件进行映射,我们就直接使用@ConfigurationProperties
@PropertySource和@importResource
根据名称分析@PropertySource可加载非默认的配置文件,即加载指定的配置文件,当我们想将一些特定业务所需要的配置与系统默认配置分开维护时,需要@PropertySource
@Component
@ConfigurationProperties(prefix="person")
@PropertySource(value={"classpath:person.properties"})
public class PersonPropertySource {
private String lastName;
privateInteger age;
运行结果:
Person{lastName=zhangsan,age=18,boss=false,birth=Tue Jul 17 00:00:00 CST 2018,map={t3=test3, t2=test2,
t1=test1},map2=null,list=[lisi, wangwu,zhaoliu],dog=com.wensi.springbootdemo1.yml.Dog@1b32cd16}
@importResource导入spring的配置文件,让配置文件里面的内容生效
因为springboot里面没有spring的配置文件,自己编写地配置文件也不能识别,所以需要使用@importResource加载进来。示例:
public class TestImportService {
}
bean.xml:
@SpringBootApplication
@ImportResource(locations = {"classpath:bean.xml"})
public class App
{
……
}
测试用例:
@Autowired
ApplicationContext ioc;
@Test
public void testImportSource(){
System.out.println(ioc.containsBean("testImportService"));
}
运行结果:true
SpringBoot推荐给容器中添加组件的方式:推荐使用全注解的方式
1、配置类@Configuration------>Spring配置文件
2、使用@Bean给容器中添加组件,配置类这里使用@Bean注解
/**
* @Configuration:指明当前类是一个配置类;就是来替代之前的Spring配置文件
** 在配置文件中用标签添加组件
*/
@Configuration
public class MyAppConfig {
//将方法的返回值添加到容器中;容器中这个组件默认的id就是方法名
@Bean
public HelloService helloService02(){
System.out.println("配置类@Bean给容器中添加组件了...");
return new HelloService();
}
}
配置文件占位符
1、随机数:
${random.value}、${random.int}、${random.long}
${random.int(10)}、${random.int[1024,65536]}
2、占位符获取之前配置的值,如果没有可以是用:指定默认值
person.last‐name=张三${random.uuid}
person.age=${random.int}
person.birth=2017/12/15
person.boss=false
person.maps.k1=v1
person.maps.k2=14
person.lists=a,b,c
person.dog.age=15
person.dog.name=${person.hello:hello}_dog
如果${person.hello: hello}取不到值就默认值hello
多profile文件
应该对开发\测试\生产环境下切换
文件名可以是application-{profile}.properties/yml
默认使用的是application.properties/yml
激活指定profile
在配置文件中指定spring.profile.activie=dev,示例,新建application-dev.properties:
application.properties中增加spring.profile.activie=dev
Yml格式的多profile:yml支持文档块模式
spring:
profiles:
active: prod
---
server:
port: 8083
spring:
profiles: prod
---
server:
port: 8084
spring:
profiles:dev
运行如图:
spring.profiles.active是激活使用指定的配置文件。
另一种可以使用命令行的方式:java -jar ***.jar –spring.profiles.active=dev
还有一种虚拟机参数:-Dspring.profiles.active=dev
配置文件加载位置
Spring
boot启动会扫描以下位置的application.properties或yml作为spring
boot的默认配置文件
-file:./config/
-file:./
-classpath:/config/
-classpath:/
以上是按照优先级从高到低的顺序,所有位置的文件都会被加载,高优先级配置内容会覆盖低优先级配置内容。
外部配置加载顺序
SpringBoot也可以从外部以下位置加载配置; 优先级从高到低;高优先级的配置覆盖低优先级的配置,所有的配置会形成互补配置
1.命令行参数
所有的配置都可以在命令行上进行指定
java -jarspring-boot-02-config-02-0.0.1-SNAPSHOT.jar --server.port=8087--server.context-path=/abc
多个配置用空格分开;--配置项=值
2.来自java:comp/env的JNDI属性
3.Java系统属性(System.getProperties())
4.操作系统环境变量
5.RandomValuePropertySource配置的random.*属性值由jar包外向jar包内进行寻找;优先加载带profile
6.jar包外部的application-{profile}.properties或application.yml(带spring.profile)配置文件
7.jar包内部的application-{profile}.properties或application.yml(spring.profile)配置文件再来加载不带profile
8.jar包外部的application.properties或application.yml(不spring.profile)配置文件
9.jar包内部的application.properties或application.yml(不spring.profile)配置文件
10.@Configuration注解类上的@PropertySource
11.通过SpringApplication.setDefaultProperties指定的默认属性
示例:
java
-jar springbootdemo1-0.0.1-SNAPSHOT.jar -server.port=8088,
启动端口就覆盖配置文件的里的端口设置了,启动端口为8088
二、自动配置原理
配置文件能配置的属性参照:
观察@ SpringBootApplication源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes =
TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes =
AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication
1)SpringBoot启动时候加载主配置类,开启了自动配置功能@EnableAutoConfiguration
2) @EnableAutoConfiguration利用EnableAutoConfigurationImportSelector选择器给容器导入META-INF/Spring.factories中的默认配置对应的组件。(可查看selectImports()方法-->
List configurations = getCandidateConfigurations(annotationMetadata, attributes);获取候选的配置-->
SpringFactoriesLoader.loadFactoryNames()//扫描所有jar包类路径下META‐INF/spring.factories把扫描到的这些文件的内容包装成properties对象从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器中)
3)如上每一个自动配置类进行自动配置
4) 以HttpEncodingAutoConfiguration(Http编码自动配置)为例解释自动配置原理;
@Configuration //表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件
@EnableConfigurationProperties(HttpEncodingProperties.class)
//启动指定类的
ConfigurationProperties功能;将配置文件中对应的值和HttpEncodingProperties绑定起来;并把
HttpEncodingProperties加入到ioc容器中
@ConditionalOnWebApplication
//Spring底层@Conditional注解(Spring注解版),根据不同的条件,如果
满足指定的条件,整个配置类里面的配置就会生效; 判断当前应用是否是web应用,如果是,当前配置类生效
@ConditionalOnClass(CharacterEncodingFilter.class)
//判断当前项目有没有这个类
CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;
@ConditionalOnProperty(prefix= "spring.http.encoding", value = "enabled", matchIfMissing=
true) //判断配置文件中是否存在某个配置spring.http.encoding.enabled;如果不存在,判断也是成立的
//即使我们配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的;
public class HttpEncodingAutoConfiguration{
//他已经和SpringBoot的配置文件映射了
private final HttpEncodingPropertiesproperties;
//只有一个有参构造器的情况下,参数的值就会从容器中拿
publicHttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
this.properties =properties;
}
@Bean //给容器中添加一个组件,这个组件的某些值需要从properties中获取
@ConditionalOnMissingBean(CharacterEncodingFilter.class)
//判断容器没有这个组件?
publicCharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = newOrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
return filter;
}
根据当前不同的条件判断,决定这个配置类是否生效?
一但这个配置类生效;这个配置类就会给容器中添加各种组件;这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的
这里能看出配置文件能配置的属性都是来源于HttpEncodingProperties类。
精髓:
1:spring boot启动会加载大量的自动配置类
2:我们看我们需要的功能有没有springboot默写好的自动配置类
3:我们看这个自动配置类中配置了哪些组件(我们要用的有就不用再配置了)
4:给容器中自动配置类添加组件时,会从properties类中获取某些属性,我们可以在配置文件中指定这些属性的值
xxxAutoConfigurartion自动配置类,给容器中添加组件,xxxAutoConfigurartion下的xxxxProperties:封装配置文件中相关属性;
网友评论