描述问题:通过调用snakeyaml的api生产了yaml的配置文件,然后将生成的yaml配置文件放到spring boot 的bootstrap.yml 配置文件中,启动项目,程序报错。
问题结论:Spring Boot 集成snakeyaml 但却没用snakeyaml 对set集合中复杂对象的解析,自己封装了一套解析方案,导致Spring Boot 加载yaml时对set集合中复杂对象解析失败。
疑问:是Spring Boot 压根没有想对这种类型支持,还是Spring Boot的Bug?有大佬给解答下吗?
虽然通过Spring Boot和snakeyaml 对比将问题解决,但感觉很麻烦,还修改了源码。
1. 通过调用snakeyaml的api,生产yaml的配置文件,如下:
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import java.util.Set;
public class ApiDefinitionExt extends ApiDefinition implements Comparable<ApiDefinitionExt> {
private String apiNameExt;
private Set<ApiPredicateItem> predicateItemsExt;
public ApiDefinitionExt() {
}
public ApiDefinitionExt(String apiNameExt) {
super(apiNameExt);
this.apiNameExt = apiNameExt;
}
public String getApiNameExt() {
return super.getApiName();
}
public void setApiNameExt(String apiNameExt) {
super.setApiName(apiNameExt);
this.apiNameExt = apiNameExt;
}
public Set<ApiPredicateItem> getPredicateItemsExt() {
return super.getPredicateItems();
}
public void setPredicateItemsExt(Set<ApiPredicateItem> predicateItemsExt) {
super.setPredicateItems(predicateItemsExt);
this.predicateItemsExt = predicateItemsExt;
}
@Override
public int compareTo(ApiDefinitionExt o) {
return apiNameExt.compareTo(o.apiNameExt);
}
}
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
public class ApiPathPredicateItemExt implements ApiPredicateItem, Comparable<ApiPathPredicateItemExt> {
private String pattern;
private int matchStrategy = SentinelGatewayConstants.URL_MATCH_STRATEGY_EXACT;
public String getPattern() {
return pattern;
}
public void setPattern(String pattern) {
this.pattern = pattern;
}
public int getMatchStrategy() {
return matchStrategy;
}
public void setMatchStrategy(int matchStrategy) {
this.matchStrategy = matchStrategy;
}
@Override
public int compareTo(ApiPathPredicateItemExt o) {
return pattern.compareTo(o.pattern);
}
}
import java.util.Set;
import java.util.TreeSet;
import org.yaml.snakeyaml.Yaml;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.gateway.config.sentinel.ApiDefinitionExt;
import com.gateway.config.sentinel.ApiPathPredicateItemExt;
import junit.framework.TestCase;
public class SnakeyamlBeanToYaml extends TestCase {
public void testBeanToYaml() {
Set<ApiPredicateItem> predicateItems = new TreeSet<ApiPredicateItem>();
ApiPathPredicateItemExt ext1 = new ApiPathPredicateItemExt();
ext1.setPattern("/ahas");
predicateItems.add(ext1);
ApiPathPredicateItemExt ext2 = new ApiPathPredicateItemExt();
ext2.setPattern("/product/**");
ext2.setMatchStrategy(1);
predicateItems.add(ext2);
ApiDefinitionExt apiDefinition = new ApiDefinitionExt();
apiDefinition.setApiNameExt("some_customized_api");
apiDefinition.setPredicateItemsExt(predicateItems);
Set<ApiPredicateItem> predicateItems2 = new TreeSet<ApiPredicateItem>();
ApiPathPredicateItemExt ext3 = new ApiPathPredicateItemExt();
ext3.setPattern("/**");
ext3.setMatchStrategy(1);
predicateItems2.add(ext3);
ApiDefinitionExt apiDefinition1 = new ApiDefinitionExt();
apiDefinition1.setApiNameExt("another_customized_api");
apiDefinition1.setPredicateItemsExt(predicateItems2);
Set<ApiDefinitionExt> definitions = new TreeSet<ApiDefinitionExt>();
definitions.add(apiDefinition);
definitions.add(apiDefinition1);
ApiDefinitionConfigProperties apiDefinitionConfig = new ApiDefinitionConfigProperties();
apiDefinitionConfig.setApiDefinitionExt(definitions);
Yaml yaml = new Yaml();
String output = yaml.dumpAsMap(apiDefinitionConfig);
System.out.println(output);
}
}
通过测试类SnakeyamlBeanToYaml生成的yaml配置文件,如下:
apiDefinitionExt: !!set
? apiNameExt: another_customized_api
predicateItemsExt: !!set
? !!com.gateway.config.sentinel.ApiPathPredicateItemExt
matchStrategy: 1
pattern: /**
: null
: null
? apiNameExt: some_customized_api
predicateItemsExt: !!set
? !!com.gateway.config.sentinel.ApiPathPredicateItemExt
matchStrategy: 0
pattern: /ahas
: null
? !!com.gateway.config.sentinel.ApiPathPredicateItemExt
matchStrategy: 1
pattern: /product/**
: null
: null
2. 将配置文件复制到的bootstrap.yml 配置文件中,然后自定义配置类加载Yaml
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.gateway.config.sentinel.ApiDefinitionExt;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Component
@ConfigurationProperties("api")
public class ApiDefinitionConfigProperties {
private Set<ApiDefinitionExt> apiDefinitionExt;
public Set<ApiDefinitionExt> getApiDefinitionExt() {
return apiDefinitionExt;
}
public void setApiDefinitionExt(Set<ApiDefinitionExt> apiDefinitionExt) {
this.apiDefinitionExt = apiDefinitionExt;
}
}
api:
apiDefinitionExt: !!set
? apiNameExt: another_customized_api
predicateItemsExt: !!set
? !!com.gateway.config.sentinel.ApiPathPredicateItemExt
matchStrategy: 1
pattern: /**
: null
: null
? apiNameExt: some_customized_api
predicateItemsExt: !!set
? !!com.gateway.config.sentinel.ApiPathPredicateItemExt
matchStrategy: 0
pattern: /ahas
: null
? !!com.gateway.config.sentinel.ApiPathPredicateItemExt
matchStrategy: 1
pattern: /product/**
: null
: null
然后启动SpringBoot项目报错:
Caused by: org.yaml.snakeyaml.constructor.ConstructorException: could not determine a constructor for the tag tag:yaml.org,2002:com.gateway.config.sentinel.ApiPathPredicateItemExt
in 'reader', line 104, column 15:
? !!com.gateway.config.sentin ...
^
at org.yaml.snakeyaml.constructor.SafeConstructor$ConstructUndefined.construct(SafeConstructor.java:576)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObjectNoCheck(BaseConstructor.java:234)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObject(BaseConstructor.java:220)
at org.springframework.boot.env.OriginTrackedYamlLoader$OriginTrackingConstructor.constructObject(OriginTrackedYamlLoader.java:105)
at org.yaml.snakeyaml.constructor.SafeConstructor.processDuplicateKeys(SafeConstructor.java:91)
at org.yaml.snakeyaml.constructor.SafeConstructor.flattenMapping(SafeConstructor.java:76)
at org.yaml.snakeyaml.constructor.SafeConstructor.constructSet2ndStep(SafeConstructor.java:195)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructSet(BaseConstructor.java:457)
at org.yaml.snakeyaml.constructor.SafeConstructor$ConstructYamlSet.construct(SafeConstructor.java:505)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObjectNoCheck(BaseConstructor.java:234)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObject(BaseConstructor.java:220)
at org.springframework.boot.env.OriginTrackedYamlLoader$OriginTrackingConstructor.constructObject(OriginTrackedYamlLoader.java:105)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping2ndStep(BaseConstructor.java:482)
at org.yaml.snakeyaml.constructor.SafeConstructor.constructMapping2ndStep(SafeConstructor.java:190)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping(BaseConstructor.java:463)
at org.yaml.snakeyaml.constructor.SafeConstructor$ConstructYamlMap.construct(SafeConstructor.java:556)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObjectNoCheck(BaseConstructor.java:234)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObject(BaseConstructor.java:220)
at org.springframework.boot.env.OriginTrackedYamlLoader$OriginTrackingConstructor.constructObject(OriginTrackedYamlLoader.java:105)
at org.yaml.snakeyaml.constructor.SafeConstructor.processDuplicateKeys(SafeConstructor.java:91)
at org.yaml.snakeyaml.constructor.SafeConstructor.flattenMapping(SafeConstructor.java:76)
at org.yaml.snakeyaml.constructor.SafeConstructor.constructSet2ndStep(SafeConstructor.java:195)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructSet(BaseConstructor.java:457)
at org.yaml.snakeyaml.constructor.SafeConstructor$ConstructYamlSet.construct(SafeConstructor.java:505)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObjectNoCheck(BaseConstructor.java:234)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObject(BaseConstructor.java:220)
at org.springframework.boot.env.OriginTrackedYamlLoader$OriginTrackingConstructor.constructObject(OriginTrackedYamlLoader.java:105)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping2ndStep(BaseConstructor.java:482)
at org.yaml.snakeyaml.constructor.SafeConstructor.constructMapping2ndStep(SafeConstructor.java:190)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping(BaseConstructor.java:463)
at org.yaml.snakeyaml.constructor.SafeConstructor$ConstructYamlMap.construct(SafeConstructor.java:556)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObjectNoCheck(BaseConstructor.java:234)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObject(BaseConstructor.java:220)
at org.springframework.boot.env.OriginTrackedYamlLoader$OriginTrackingConstructor.constructObject(OriginTrackedYamlLoader.java:105)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping2ndStep(BaseConstructor.java:482)
at org.yaml.snakeyaml.constructor.SafeConstructor.constructMapping2ndStep(SafeConstructor.java:190)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping(BaseConstructor.java:463)
at org.yaml.snakeyaml.constructor.SafeConstructor$ConstructYamlMap.construct(SafeConstructor.java:556)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObjectNoCheck(BaseConstructor.java:234)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObject(BaseConstructor.java:220)
at org.springframework.boot.env.OriginTrackedYamlLoader$OriginTrackingConstructor.constructObject(OriginTrackedYamlLoader.java:105)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping2ndStep(BaseConstructor.java:482)
at org.yaml.snakeyaml.constructor.SafeConstructor.constructMapping2ndStep(SafeConstructor.java:190)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping(BaseConstructor.java:463)
at org.yaml.snakeyaml.constructor.SafeConstructor$ConstructYamlMap.construct(SafeConstructor.java:556)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObjectNoCheck(BaseConstructor.java:234)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObject(BaseConstructor.java:220)
at org.springframework.boot.env.OriginTrackedYamlLoader$OriginTrackingConstructor.constructObject(OriginTrackedYamlLoader.java:105)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping2ndStep(BaseConstructor.java:482)
at org.yaml.snakeyaml.constructor.SafeConstructor.constructMapping2ndStep(SafeConstructor.java:190)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping(BaseConstructor.java:463)
at org.yaml.snakeyaml.constructor.SafeConstructor$ConstructYamlMap.construct(SafeConstructor.java:556)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObjectNoCheck(BaseConstructor.java:234)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObject(BaseConstructor.java:220)
at org.springframework.boot.env.OriginTrackedYamlLoader$OriginTrackingConstructor.constructObject(OriginTrackedYamlLoader.java:105)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructDocument(BaseConstructor.java:174)
at org.yaml.snakeyaml.constructor.BaseConstructor.getData(BaseConstructor.java:139)
at org.yaml.snakeyaml.Yaml$1.next(Yaml.java:494)
at org.springframework.beans.factory.config.YamlProcessor.process(YamlProcessor.java:200)
at org.springframework.beans.factory.config.YamlProcessor.process(YamlProcessor.java:164)
at org.springframework.boot.env.OriginTrackedYamlLoader.l
4.查看源码分析问题:
上面说了Spring Boot 自己封装了自己Yaml解析方式, 这里涉及到一个类OriginTrackedYamlLoader 。以及snakeyaml 本身对Yaml解析方式,下面对比下发现,SpringBoot 创建Yaml对象时的第一个参数BaseConstructor 参入的是自己定义的OriginTrackingConstructor,而snakeyaml 用的是 Constructor对象,他们都是SafeConstructor的子类但内部实现方式却不同,Constructor对Set集合复杂对象有解析,但用OriginTrackingConstructor却不支持,因为它没有对应set复杂对象的解析对象 ConstructYamlObject,所以会报错。
/**
* Class to load {@code .yml} files into a map of {@code String} to
* {@link OriginTrackedValue}.
*
* @author Madhura Bhave
* @author Phillip Webb
*/
class OriginTrackedYamlLoader extends YamlProcessor {
@Override
protected Yaml createYaml() {
LoaderOptions loaderOptions = new LoaderOptions();
loaderOptions.setAllowDuplicateKeys(false);
loaderOptions.setMaxAliasesForCollections(Integer.MAX_VALUE);
loaderOptions.setAllowRecursiveKeys(true);
return createYaml(loaderOptions);
}
private Yaml createYaml(LoaderOptions loaderOptions) {
BaseConstructor constructor = new OriginTrackingConstructor(loaderOptions);
Representer representer = new Representer();
DumperOptions dumperOptions = new DumperOptions();
LimitedResolver resolver = new LimitedResolver();
// Yaml对象的创建
return new Yaml(constructor, representer, dumperOptions, loaderOptions, resolver);
}
/**
* {@link Constructor} that tracks property origins.
*/
private class OriginTrackingConstructor extends SafeConstructor{
OriginTrackingConstructor(LoaderOptions loadingConfig) {
super(loadingConfig);
}
@Override
protected Object constructObject(Node node) {
if (node instanceof ScalarNode) {
if (!(node instanceof KeyScalarNode)) {
return constructTrackedObject(node, super.constructObject(node));
}
}
else if (node instanceof MappingNode) {
replaceMappingNodeKeys((MappingNode) node);
}
return super.constructObject(node);
}
private void replaceMappingNodeKeys(MappingNode node) {
node.setValue(node.getValue().stream().map(KeyScalarNode::get).collect(Collectors.toList()));
}
private Object constructTrackedObject(Node node, Object value) {
Origin origin = getOrigin(node);
return OriginTrackedValue.of(getValue(value), origin);
}
private Object getValue(Object value) {
return (value != null) ? value : "";
}
private Origin getOrigin(Node node) {
Mark mark = node.getStartMark();
Location location = new Location(mark.getLine(), mark.getColumn());
return new TextResourceOrigin(OriginTrackedYamlLoader.this.resource, location);
}
}
对比snakeyaml 源码Yaml对象的创建,代码如下:
public class Yaml {
/**
* Create Yaml instance.
*/
public Yaml() {
this(new Constructor(), new Representer(), new DumperOptions(), new LoaderOptions(),
new Resolver());
}
}
这里我们修改Spring Boot OriginTrackedYamlLoader 的源码,让它可以返回可以解析set对象的ConstructYamlObject对象,这类是把OriginTrackingConstructor 的继承类换成了Constructor,如下:
/**
* Class to load {@code .yml} files into a map of {@code String} to
* {@link OriginTrackedValue}.
*
* @author Madhura Bhave
* @author Phillip Webb
*/
class OriginTrackedYamlLoader extends YamlProcessor {
/**
* {@link Constructor} that tracks property origins.
* 这里我们把SafeConstructor修改为Constructor,让它在调用super.constructObject(node)时针
* 对Set复杂集合可以返回ConstructYamlObject对象
*/
private class OriginTrackingConstructor extends Constructor{
OriginTrackingConstructor(LoaderOptions loadingConfig) {
super(loadingConfig);
}
@Override
protected Object constructObject(Node node) {
if (node instanceof ScalarNode) {
if (!(node instanceof KeyScalarNode)) {
return constructTrackedObject(node, super.constructObject(node));
}
}
else if (node instanceof MappingNode) {
replaceMappingNodeKeys((MappingNode) node);
}
return super.constructObject(node);
}
}
修改完代码启动程序,上面的报错不在显示,但又引入了新的问题,参数类型不匹配,出现这个问题是,OriginTrackingConstructor对数据进行了二次封装,通过方法 constructObject方法将Node对象转化为了OriginTrackedValue对象了,这时snakeyaml 中Constructor通过反射映射值时类型不匹配,导致matchStrategy对应的int类型变成了OriginTrackedValue对象,所以有问题,这里我们修改下Constructor的源码。
Caused by: org.yaml.snakeyaml.constructor.ConstructorException: Cannot create property=matchStrategy for JavaBean=com.gateway.config.sentinel.ApiPathPredicateItemExt@f79e
in 'reader', line 104, column 15:
? !!com.gateway.config.sentin ...
^
argument type mismatch
in 'reader', line 105, column 30:
matchStrategy: 1
^
at org.yaml.snakeyaml.constructor.Constructor$ConstructMapping.constructJavaBean2ndStep(Constructor.java:297)
at org.yaml.snakeyaml.constructor.Constructor$ConstructMapping.construct(Constructor.java:172)
at org.yaml.snakeyaml.constructor.Constructor$ConstructYamlObject.construct(Constructor.java:336)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObjectNoCheck(BaseConstructor.java:234)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObject(BaseConstructor.java:220)
at org.springframework.boot.env.OriginTrackedYamlLoader$OriginTrackingConstructor.constructObject(OriginTrackedYamlLoader.java:105)
at org.yaml.snakeyaml.constructor.SafeConstructor.processDuplicateKeys(SafeConstructor.java:91)
at org.yaml.snakeyaml.constructor.SafeConstructor.flattenMapping(SafeConstructor.java:76)
at org.yaml.snakeyaml.constructor.SafeConstructor.constructSet2ndStep(SafeConstructor.java:195)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructSet(BaseConstructor.java:457)
at org.yaml.snakeyaml.constructor.SafeConstructor$ConstructYamlSet.construct(SafeConstructor.java:505)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObjectNoCheck(BaseConstructor.java:234)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObject(BaseConstructor.java:220)
at org.springframework.boot.env.OriginTrackedYamlLoader$OriginTrackingConstructor.constructObject(OriginTrackedYamlLoader.java:105)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping2ndStep(BaseConstructor.java:482)
at org.yaml.snakeyaml.constructor.SafeConstructor.constructMapping2ndStep(SafeConstructor.java:190)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping(BaseConstructor.java:463)
at org.yaml.snakeyaml.constructor.SafeConstructor$ConstructYamlMap.construct(SafeConstructor.java:556)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObjectNoCheck(BaseConstructor.java:234)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObject(BaseConstructor.java:220)
at org.springframework.boot.env.OriginTrackedYamlLoader$OriginTrackingConstructor.constructObject(OriginTrackedYamlLoader.java:105)
at org.yaml.snakeyaml.constructor.SafeConstructor.processDuplicateKeys(SafeConstructor.java:91)
at org.yaml.snakeyaml.constructor.SafeConstructor.flattenMapping(SafeConstructor.java:76)
at org.yaml.snakeyaml.constructor.SafeConstructor.constructSet2ndStep(SafeConstructor.java:195)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructSet(BaseConstructor.java:457)
at org.yaml.snakeyaml.constructor.SafeConstructor$ConstructYamlSet.construct(SafeConstructor.java:505)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObjectNoCheck(BaseConstructor.java:234)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObject(BaseConstructor.java:220)
at org.springframework.boot.env.OriginTrackedYamlLoader$OriginTrackingConstructor.constructObject(OriginTrackedYamlLoader.java:105)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping2ndStep(BaseConstructor.java:482)
at org.yaml.snakeyaml.constructor.SafeConstructor.constructMapping2ndStep(SafeConstructor.java:190)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping(BaseConstructor.java:463)
at org.yaml.snakeyaml.constructor.SafeConstructor$ConstructYamlMap.construct(SafeConstructor.java:556)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObjectNoCheck(BaseConstructor.java:234)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObject(BaseConstructor.java:220)
at org.springframework.boot.env.OriginTrackedYamlLoader$OriginTrackingConstructor.constructObject(OriginTrackedYamlLoader.java:105)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping2ndStep(BaseConstructor.java:482)
at org.yaml.snakeyaml.constructor.SafeConstructor.constructMapping2ndStep(SafeConstructor.java:190)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping(BaseConstructor.java:463)
at org.yaml.snakeyaml.constructor.SafeConstructor$ConstructYamlMap.construct(SafeConstructor.java:556)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObjectNoCheck(BaseConstructor.java:234)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObject(BaseConstructor.java:220)
at org.springframework.boot.env.OriginTrackedYamlLoader$OriginTrackingConstructor.constructObject(OriginTrackedYamlLoader.java:105)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping2ndStep(BaseConstructor.java:482)
at org.yaml.snakeyaml.constructor.SafeConstructor.constructMapping2ndStep(SafeConstructor.java:190)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping(BaseConstructor.java:463)
at org.yaml.snakeyaml.constructor.SafeConstructor$ConstructYamlMap.construct(SafeConstructor.java:556)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObjectNoCheck(BaseConstructor.java:234)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObject(BaseConstructor.java:220)
at org.springframework.boot.env.OriginTrackedYamlLoader$OriginTrackingConstructor.constructObject(OriginTrackedYamlLoader.java:105)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping2ndStep(BaseConstructor.java:482)
at org.yaml.snakeyaml.constructor.SafeConstructor.constructMapping2ndStep(SafeConstructor.java:190)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping(BaseConstructor.java:463)
at org.yaml.snakeyaml.constructor.SafeConstructor$ConstructYamlMap.construct(SafeConstructor.java:556)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObjectNoCheck(BaseConstructor.java:234)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObject(BaseConstructor.java:220)
at org.springframework.boot.env.OriginTrackedYamlLoader$OriginTrackingConstructor.constructObject(OriginTrackedYamlLoader.java:105)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructDocument(BaseConstructor.java:174)
at org.yaml.snakeyaml.constructor.BaseConstructor.getData(BaseConstructor.java:139)
at org.yaml.snakeyaml.Yaml$1.next(Yaml.java:494)
at org.springframework.beans.factory.config.YamlProcessor.process(YamlProcessor.java:200)
at org.springframework.beans.factory.config.YamlProcessor.process(YamlProcessor.java:164)
at org.springframework.boot.env.OriginTrackedYamlLoader.load(OriginTrackedYamlLoader.java:82)
at org.springframework.boot.env.YamlPropertySourceLoader.load(YamlPropertySourceLoader.java:50)
at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.loadDocuments(ConfigFileApplicationListener.java:607)
at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.load(ConfigFileApplicationListener.java:523)
... 37 common frames omitted
Caused by: java.lang.IllegalArgumentException: argument type mismatch
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.yaml.snakeyaml.introspector.MethodProperty.set(MethodProperty.java:77)
at org.yaml.snakeyaml.constructor.Constructor$ConstructMapping.constructJavaBean2ndStep(Constructor.java:290)
... 102 common frames omitted
修改Constructor的源码在方法constructJavaBean2ndStep里中加入如下判断代码:
if(null != value && value instanceof OriginTrackedValue){
OriginTrackedValue originTrackedValue = (OriginTrackedValue)value;
value = originTrackedValue.getValue();
}
对应完整代码如下:
package org.yaml.snakeyaml.constructor;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.springframework.boot.origin.OriginTrackedValue;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.TypeDescription;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.introspector.Property;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.NodeId;
import org.yaml.snakeyaml.nodes.NodeTuple;
import org.yaml.snakeyaml.nodes.ScalarNode;
import org.yaml.snakeyaml.nodes.SequenceNode;
import org.yaml.snakeyaml.nodes.Tag;
/**
* Construct a custom Java instance.
*/
public class Constructor extends SafeConstructor {
protected Object constructJavaBean2ndStep(MappingNode node, Object object) {
flattenMapping(node);
Class<? extends Object> beanType = node.getType();
List<NodeTuple> nodeValue = node.getValue();
for (NodeTuple tuple : nodeValue) {
ScalarNode keyNode;
if (tuple.getKeyNode() instanceof ScalarNode) {
// key must be scalar
keyNode = (ScalarNode) tuple.getKeyNode();
} else {
throw new YAMLException(
"Keys must be scalars but found: " + tuple.getKeyNode());
}
Node valueNode = tuple.getValueNode();
// keys can only be Strings
keyNode.setType(String.class);
String key = (String) constructObject(keyNode);
try {
TypeDescription memberDescription = typeDefinitions.get(beanType);
Property property = memberDescription == null ? getProperty(beanType, key)
: memberDescription.getProperty(key);
if (!property.isWritable()) {
throw new YAMLException("No writable property '" + key + "' on class: "
+ beanType.getName());
}
valueNode.setType(property.getType());
final boolean typeDetected = (memberDescription != null)
? memberDescription.setupPropertyType(key, valueNode)
: false;
if (!typeDetected && valueNode.getNodeId() != NodeId.scalar) {
// only if there is no explicit TypeDescription
Class<?>[] arguments = property.getActualTypeArguments();
if (arguments != null && arguments.length > 0) {
// type safe (generic) collection may contain the
// proper class
if (valueNode.getNodeId() == NodeId.sequence) {
Class<?> t = arguments[0];
SequenceNode snode = (SequenceNode) valueNode;
snode.setListType(t);
} else if (Set.class.isAssignableFrom(valueNode.getType())) {
Class<?> t = arguments[0];
MappingNode mnode = (MappingNode) valueNode;
mnode.setOnlyKeyType(t);
mnode.setUseClassConstructor(true);
} else if (Map.class.isAssignableFrom(valueNode.getType())) {
Class<?> ketType = arguments[0];
Class<?> valueType = arguments[1];
MappingNode mnode = (MappingNode) valueNode;
mnode.setTypes(ketType, valueType);
mnode.setUseClassConstructor(true);
}
}
}
Object value = (memberDescription != null)
? newInstance(memberDescription, key, valueNode)
: constructObject(valueNode);
// 在这里我们加入如下判断针对object为OriginTrackedValue对象时
if(null != value && value instanceof OriginTrackedValue){
OriginTrackedValue originTrackedValue = (OriginTrackedValue)value;
value = originTrackedValue.getValue();
}
// Correct when the property expects float but double was
// constructed
if (property.getType() == Float.TYPE || property.getType() == Float.class) {
if (value instanceof Double) {
value = ((Double) value).floatValue();
}
}
// Correct when the property a String but the value is binary
if (property.getType() == String.class && Tag.BINARY.equals(valueNode.getTag())
&& value instanceof byte[]) {
value = new String((byte[]) value);
}
if (memberDescription == null
|| !memberDescription.setProperty(object, key, value)) {
property.set(object, value);
}
} catch (DuplicateKeyException e) {
throw e;
} catch (Exception e) {
throw new ConstructorException(
"Cannot create property=" + key + " for JavaBean=" + object,
node.getStartMark(), e.getMessage(), valueNode.getStartMark(), e);
}
}
return object;
}
}
在启动项目,正常的启动成功。