基于Spring Boot的数据库初始化(三)
本篇本文将继续介绍在升级到spring boot version 2.2.0.M3 之后的版本, 该如何沿用之前的思路方法完成数据库的初始化功能。在前一章基于Spring Boot的数据库初始化(二)博文中提到更新版本后抛出会抛出 java.lang.UnsupportedOperationException: null
的错误。这一章就将这个问题解决掉。
了解更新后异常的原因
根本原因是YamlPropertySourceLoader.java类 更新前版本version 2.2.0.M3
public class YamlPropertySourceLoader implements PropertySourceLoader {
@Override
public String[] getFileExtensions() {
return new String[] { "yml", "yaml" };
}
@Override
public List<PropertySource<?>> load(String name, Resource resource)
throws IOException {
if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) {
throw new IllegalStateException("Attempted to load " + name
+ " but snakeyaml was not found on the classpath");
}
List<Map<String, Object>> loaded = new OriginTrackedYamlLoader(resource).load();
if (loaded.isEmpty()) {
return Collections.emptyList();
}
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,
loaded.get(i)));
}
return propertySources;
}
}
更新后版本version 2.2.0.RELEASE
public class YamlPropertySourceLoader implements PropertySourceLoader {
@Override
public String[] getFileExtensions() {
return new String[] { "yml", "yaml" };
}
@Override
public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) {
throw new IllegalStateException(
"Attempted to load " + name + " but snakeyaml was not found on the classpath");
}
List<Map<String, Object>> loaded = new OriginTrackedYamlLoader(resource).load();
if (loaded.isEmpty()) {
return Collections.emptyList();
}
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,
Collections.unmodifiableMap(loaded.get(i)), true));
}
return propertySources;
}
}
两个版本间唯一的区别就是以下两行代码
// 2.2.0.M3
propertySources.add(new OriginTrackedMapPropertySource(name + documentNumber,
loaded.get(i)));
// 2.2.0.RELEASE
propertySources.add(new OriginTrackedMapPropertySource(name + documentNumber,
Collections.unmodifiableMap(loaded.get(i)), true));
那很容易想到, 是否可以自定义一个配置文件加载器, 然后复制前一版本的代码就可以了. Spring Boot确实提供了自定义PropertySource Loaders的方法, 但是并没有我预想的那么顺利, 因为YamlPropertySourceLoader.java
类中使用的OriginTrackedYamlLoader.java
类不是public
的所以我们无法直接使用.
解决方法
自定义PropertySourceLoader
Spring Boot会从META-INF/spring.factories
文件加载了许多自定义项,以供内部使用.
利用此文件来加载我们自定义的PropertySourceLoader
.
org.springframework.boot.env.PropertySourceLoader=\
com.developerchen.core.extension.MyYamlPropertySourceLoader
MyYamlPropertySourceLoader类代码解析
public class MyYamlPropertySourceLoader extends YamlPropertySourceLoader {
@Override
public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
List<PropertySource<?>> propertySources = super.load(name, resource);
List<PropertySource<?>> newPropertySources = new ArrayList<>(propertySources.size());
for (PropertySource<?> propertySource : propertySources) {
if (propertySource instanceof OriginTrackedMapPropertySource) {
Map<?, ?> source = (Map) propertySource.getSource();
Map<Object, Object> newSource = new LinkedHashMap<>((int) (source.size() / 0.75 + 1));
for (Object key : source.keySet()) {
Object value = source.get(key);
newSource.put(key, value);
}
propertySource = new OriginTrackedMapPropertySource(propertySource.getName(),
newSource, true);
}
newPropertySources.add(propertySource);
}
return newPropertySources;
}
}
这个类重写了YamlPropertySourceLoader
类中的load
方法, 使用父类中的方法将yml文件解析出来. 然后将内容遍历出来并用LinkedHashMap
替换掉原来用Collections.unmodifiableMap
方法处理了的Map.
这样就解决了数据库初始化的问题, 希望Spring官方在未来的更新中提供完善实用的数据库初始化功能.
本文采用 知识共享署名4.0国际许可协议进行许可. 本站文章除注明转载/出处外, 均为本站原创或翻译,转载前请务必署名!