具体报错内容如下:
java.lang.NoSuchMethodError: org.yaml.snakeyaml.nodes.ScalarNode.getScalarStyle()Lorg/yaml/snakeyaml/DumperOptions$ScalarStyle;
at org.springframework.boot.env.OriginTrackedYamlLoader$KeyScalarNode.<init>(OriginTrackedYamlLoader.java:127)
at org.springframework.boot.env.OriginTrackedYamlLoader$KeyScalarNode.get(OriginTrackedYamlLoader.java:138)
at org.springframework.boot.env.OriginTrackedYamlLoader$KeyScalarNode.get(OriginTrackedYamlLoader.java:133)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1654)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
at org.springframework.boot.env.OriginTrackedYamlLoader$OriginTrackingConstructor.replaceMappingNodeKeys(OriginTrackedYamlLoader.java:99)
at org.springframework.boot.env.OriginTrackedYamlLoader$OriginTrackingConstructor.constructObject(OriginTrackedYamlLoader.java:92)
at org.yaml.snakeyaml.constructor.BaseConstructor.constructDocument(BaseConstructor.java:161)
at org.yaml.snakeyaml.constructor.BaseConstructor.getData(BaseConstructor.java:127)
at org.yaml.snakeyaml.Yaml$1.next(Yaml.java:547)
at org.springframework.beans.factory.config.YamlProcessor.process(YamlProcessor.java:161)
at org.springframework.beans.factory.config.YamlProcessor.process(YamlProcessor.java:134)
at org.springframework.boot.env.OriginTrackedYamlLoader.load(OriginTrackedYamlLoader.java:75)
at org.springframework.boot.env.YamlPropertySourceLoader.load(YamlPropertySourceLoader.java:50)
at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.loadDocuments(ConfigFileApplicationListener.java:572)
at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.load(ConfigFileApplicationListener.java:526)
at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.loadForFileExtension(ConfigFileApplicationListener.java:500)
at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.load(ConfigFileApplicationListener.java:467)
at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.lambda$null$6(ConfigFileApplicationListener.java:448)
at java.base/java.lang.Iterable.forEach(Iterable.java:75)
at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.lambda$load$7(ConfigFileApplicationListener.java:447)
at java.base/java.lang.Iterable.forEach(Iterable.java:75)
at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.load(ConfigFileApplicationListener.java:444)
at org.springframework.boot.context.config.ConfigFileApplicationListener$Loader.load(ConfigFileApplicationListener.java:332)
at org.springframework.boot.context.config.ConfigFileApplicationListener.addPropertySources(ConfigFileApplicationListener.java:207)
at org.springframework.boot.context.config.ConfigFileApplicationListener.postProcessEnvironment(ConfigFileApplicationListener.java:190)
at org.springframework.boot.context.config.ConfigFileApplicationListener.onApplicationEnvironmentPreparedEvent(ConfigFileApplicationListener.java:177)
at org.springframework.boot.context.config.ConfigFileApplicationListener.onApplicationEvent(ConfigFileApplicationListener.java:163)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)
at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:75)
at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:54)
at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:347)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:306)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248)
at com.mmr.BasicKnowledgeApplication.main(BasicKnowledgeApplication.java:14)
参考文章:https://blog.csdn.net/liaokailin/article/details/48878447 Spring boot 资源文件加载时序图
不难发现,问题出在加载properties文件的阶段,这里我们找到org.springframework.boot.env.PropertySourceLoader 配置文件加载器。
package org.springframework.boot.env;
import java.io.IOException;
import java.util.List;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;
public interface PropertySourceLoader {
String[] getFileExtensions();
List<PropertySource<?>> load(String name, Resource resource) throws IOException;
}
此接口的实现类有两个,分别是PropertiesPropertySourceLoader和YamlPropertySourceLoader。 观察getFileExtensions()函数发现,前者用于解析".proerties"或".xml"结尾的配置文件,而后者专注与解析".yml"或".yaml"结尾的配置文件。
因此让我们来看YamlPropertySourceLoader
public class YamlPropertySourceLoader implements PropertySourceLoader {
public YamlPropertySourceLoader() {
}
public String[] getFileExtensions() {
return new String[]{"yml", "yaml"};
}
public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", (ClassLoader)null)) {
throw new IllegalStateException("Attempted to load " + name + " but snakeyaml was not found on the classpath");
} else {
List<Map<String, Object>> loaded = (new OriginTrackedYamlLoader(resource)).load();
if (loaded.isEmpty()) {
return Collections.emptyList();
} else {
List<PropertySource<?>> propertySources = new ArrayList(loaded.size());
for(int i = 0; i < loaded.size(); ++i) {
String documentNumber = loaded.size() != 1 ? " (document #" + i + ")" : "";
propertySources.add(new OriginTrackedMapPropertySource(name + documentNumber, (Map)loaded.get(i)));
}
return propertySources;
}
}
}
}
不难发现一定要有 "org.yaml.snakeyaml.Yaml" 存在,才可以解析,否则返回null。
解决办法很简单,在pom.xml中加上:
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>${snakeyaml.version}</version>
</dependency>
但其实,上述做法十分愚蠢,yaml解析器在spring-boot-starter中已经申明过了,一般不太可能需要手动引入。如下图所示:
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.19</version>
<scope>runtime</scope>
</dependency>
我本次出现问题的原因在于 我在子工程的pom.xml中用到了javafaker,
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>0.16</version>
</dependency>
上述代码的致命错误在于没有指明作用范围。默认的作用范围是complie 也即无论在编译还是运行(项目的所有生命周期)都会起作用,再看看javafaker的pom.xml
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.20</version>
<classifier>android</classifier>
</dependency>
不难发现,这是一个特殊jar包并且依赖于android,按照就近原则,子工程引用的配置将覆盖从父工程继承而来的配置,从而导致错误的发生。
解决办法非常简单,只需要为javafaker加一个作用域为test即可。
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>0.16</version>
<scope>test</scope>
</dependency>