Spring Boot没用snakeyaml对set集合中复杂对象的解析,以至于不能正常加载set集合中复杂对象

描述问题:通过调用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;
        }
}

在启动项目,正常的启动成功。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值