示例代码和结果
先上demo
配置文件
mytest:
date: 08:00:00
date1: 09:00:00
date2: 10:00:00
date3: 11:00:00
date4: 12:00:00
date5: 13:00:00
Mytest配置类
package com.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* @author PrecedeForetime
* @project demo
* @datetime 20/9/16 16:52
*/
@Configuration
@ConfigurationProperties(prefix = "mytest")
public class MyTest {
private String date;
private String date1;
private String date2;
private String date3;
private String date4;
private String date5;
// getter setter omit....
}
测试启动类
package com.controller;
import com.config.MyTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
/**
* @author PrecedeForetime
* @project demo
* @datetime 2019/3/19 17:12
*/
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class DemoControllerTest {
@Autowired
private MyTest myTest;
@Test
public void test1() {
System.out.println(myTest.getDate());
System.out.println(myTest.getDate1());
System.out.println(myTest.getDate2());
System.out.println(myTest.getDate3());
System.out.println(myTest.getDate4());
System.out.println(myTest.getDate5());
}
}
输出结果:
08:00:00
09:00:00
36000
39600
43200
46800
可以很明显看到输出结果中的date2~date4四个值的结果和配置文件中的值不一致,大致可以判断出这个值是该时间对应的秒数.
解决方法
这个问题的解决办法很简单,在yml配置文件中将对应的值加上双引号即可正常拿到,例10:00:00写成"10:00:00".
或者改用application.properties这个格式的配置文件,不用加双引号也能正常拿到对应的值.
Spring 解析YAML文件源码解析
因为比较好奇导致这个问题的原因,所以翻了下YAML文件解析的源码,找到了对应的原因,我的springboot版本是 v2.1.3.RELEASE,实际解析yml的包,也就是解析异常的依赖包为org.yaml:snakeyaml-1.23.jar,下面说下解析异常的具体原因
spring解析yml的类的调用栈大致如下
//初始化yaml文件解析类,也就是依次解析各个yaml配置文件,与本文没有太大关系,都是前期准备工作
org.springframework.boot.env.YamlPropertySourceLoader#load()
org.springframework.boot.env.OriginTrackedYamlLoader#load()
org.springframework.beans.factory.config.YamlProcessor#process(org.springframework.beans.factory.config.YamlProcessor.MatchCallback)
// 从这里开始具体解析application.yml这个文件,源码也从这里开始贴
org.springframework.beans.factory.config.YamlProcessor#process(org.springframework.beans.factory.config.YamlProcessor.MatchCallback, org.yaml.snakeyaml.Yaml, org.springframework.core.io.Resource)
org.yaml.snakeyaml.Yaml#loadAll(java.io.Reader)
org.yaml.snakeyaml.constructor.BaseConstructor#getData()
org.yaml.snakeyaml.composer.Composer#getNode()
org.yaml.snakeyaml.composer.Composer#composeNode()
org.yaml.snakeyaml.composer.Composer#composeScalarNode()
org.yaml.snakeyaml.resolver.Resolver#resolve()
org.springframework.beans.factory.config.YamlProcessor#process(MatchCallback,Yaml, Resource)
private boolean process(MatchCallback callback, Yaml yaml, Resource resource) {
int count = 0;
try {
if (logger.isDebugEnabled()) {
logger.debug("Loading from YAML: " + resource);
}
Reader reader = new UnicodeReader(resource.getInputStream());
try {
//如果debug能看到这里的object对象已经是解析完的yaml配置文件的内容了,所以需要看yaml.loadAll(reader)方法
for (Object object : yaml.loadAll(reader)) {
if (object != null && process(asMap(object), callback)) {
count++;
if (this.resolutionMethod == ResolutionMethod.FIRST_FOUND) {
break;
}
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " document" + (count > 1 ? "s" : "") +
" from YAML resource: " + resource);
}
}
finally {
reader.close();
}
}
catch (IOException ex) {
handleProcessError(resource, ex);
}
return (count > 0);
}
org.yaml.snakeyaml.Yaml#loadAll(java.io.Reader)
//这个方法的作用就是把一个yaml配置文件的中的各个对象包装成一个Iterator,实际上并没有直接解析,真正调用解析是在上一步的for循环遍历的时候,可以理解为懒加载
public Iterable