Не забудьте использовать его один раз в разработке@PropertySource
Загрузка аннотацийyml
документ
Справочная информация: бизнесу необходимо создать несколько классов конфигурации на основеyml
Файлы имеют четкую иерархическую структуру, поэтому выбор файла конфигурацииyml
тип.
Но в реальной разработке встречаются с использованием@PropertySource
Аннотация не может быть загруженаyml
Проблема с файлом конфигурации.
Процесс анализа:
Сначала проанализируем@PropertySource
Аннотированный исходный код:
public @interface PropertySource {
/** 加载资源的名称 */
String name() default "";
/**
* 加载资源的路径,可使用classpath,如:
* "classpath:/config/test.yml"
* 如有多个文件路径放在{}中,使用','号隔开,如:
* {"classpath:/config/test1.yml","classpath:/config/test2.yml"}
* 除使用classpath外,还可使用文件的地址,如:
* "file:/rest/application.properties"
*/
String[] value();
/** 此属性为根据资源路径找不到文件后是否报错, 默认为是 false */
boolean ignoreResourceNotFound() default false;
/** 此为读取文件的编码, 若配置中有中文建议使用 'utf-8' */
String encoding() default "";
/**
* 关键:此为读取资源文件的工程类, 默认为:
* 'PropertySourceFactory.class'
*/
Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;
}
Как видно из исходного кода, читаем файл ресурсовPropertySourceFactory
Интерфейс - это ключ, добавьте его, чтобы открытьPropertySourceFactory
Исходный код интерфейса:
public interface PropertySourceFactory {
PropertySource<?> createPropertySource(@Nullable String var1, EncodedResource var2) throws IOException;
}
Выясняется, что существует только один метод для создания интерфейса ресурса свойств, далее находим класс, реализующий этот метод:
public class DefaultPropertySourceFactory implements PropertySourceFactory {
public DefaultPropertySourceFactory() {
}
public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException {
return name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource);
}
}
В этом классе мы обнаруживаем, что он возвращает объектResourcePropertySource
,оказатьсяDefaultPropertySourceFactory
Два, используемые классомResourcePropertySource
Конструктор класса:
public ResourcePropertySource(String name, EncodedResource resource) throws IOException {
super(name, PropertiesLoaderUtils.loadProperties(resource));
this.resourceName = getNameForResource(resource.getResource());
}
public ResourcePropertySource(EncodedResource resource) throws IOException {
super(getNameForResource(resource.getResource()), PropertiesLoaderUtils.loadProperties(resource));
this.resourceName = null;
}
В приведенном выше коде используются оба конструктораPropertiesLoaderUtils.loadProperties()
Метод этого свойства, продолжайте нажимать, вы найдете такой фрагмент кода:
static void fillProperties(Properties props, EncodedResource resource, PropertiesPersister persister)
throws IOException {
InputStream stream = null;
Reader reader = null;
try {
String filename = resource.getResource().getFilename();
// private static final String XML_FILE_EXTENSION = ".xml";
if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
stream = resource.getInputStream();
persister.loadFromXml(props, stream);
}
else if (resource.requiresReader()) {
reader = resource.getReader();
persister.load(props, reader);
}
else {
stream = resource.getInputStream();
persister.load(props, stream);
}
}
finally {
if (stream != null) {
stream.close();
}
if (reader != null) {
reader.close();
}
}
}
Как видно из вышеизложенного,@PropertySource
Аннотации также можно использовать для загрузкиxml
файл, а затем в соответствии сpersister.load(props, stream)
Если вы продолжите нажимать на метод, вы найдете следующий код:
private void load0 (LineReader lr) throws IOException {
char[] convtBuf = new char[1024];
int limit;
int keyLen;
int valueStart;
char c;
boolean hasSep;
boolean precedingBackslash;
/**
* 每次读取一行
*/
while ((limit = lr.readLine()) >= 0) {
c = 0;
keyLen = 0;
valueStart = limit;
hasSep = false;
//System.out.println("line=<" + new String(lineBuf, 0, limit) + ">");
precedingBackslash = false;
/**
* 遍历一行字的每一个字符
* 若字符中出现 '='、':'、' '、'\t'、'\f' 则跳出循环
*/
while (keyLen < limit) {
c = lr.lineBuf[keyLen];
//need check if escaped.
// 如果当前遍历字符为 '=' 或 ':' 则跳出循环
if ((c == '=' || c == ':') && !precedingBackslash) {
valueStart = keyLen + 1;
hasSep = true;
break;
}
// 如果当前遍历字符为 ' ' 或 '\t' 或 '\f' 跳出循环,
// 但在接下来的循环中还需要继续遍历知道找到 '=' 或 ':'
else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) {
valueStart = keyLen + 1;
break;
}
// 检查是否转义
if (c == '\\') {
precedingBackslash = !precedingBackslash;
} else {
precedingBackslash = false;
}
// 每次循环,keyLen + 1
keyLen++;
}
/**
* 判断valueStart(值的开始下标)是否小于读取行的长度,若小于,则进入循环
*/
while (valueStart < limit) {
c = lr.lineBuf[valueStart];
// 判断当前字符是否等于空格、制表符、换页符。都不等于则进入循环
if (c != ' ' && c != '\t' && c != '\f') {
// 当 hasSep 为false时代表上个 while (keyLen < limit) 循环跳出时c为 空格或制表符或换页符
// 这里继续循环直到找到'='或':'号为止
// 由此可见 在配置文件中'=' 或 ':' 号前可有空格、制表符、换页符
if (!hasSep && (c == '=' || c == ':')) {
hasSep = true;
} else {
break;
}
}
// 每次循环,valueStart + 1
valueStart++;
}
// 获取配置文件中的key,value并保存
String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
put(key, value);
}
}
вышеload0
метод читает по одной строке за раз, а затем в соответствии с'='
или':'
получитьkey
иvalue
,иyml
Этот метод не может считывать объекты с четкой иерархией.
Из вышеприведенного анализа видно, что@PropertySource
Ключ к аннотации, читающей файл свойств,PropertySourceFactory
в интерфейсеcreatePropertySource
метод, поэтому мы хотим реализовать@PropertySource
Аннотация прочитанаyml
файл должен быть реализованcreatePropertySource
метод, в@PropertySource
Аннотировать это черезDefaultPropertySourceFactory
класс для реализации этого метода, нам нужно только наследовать этот класс и переопределить егоcreatePropertySource
метод, код реализации выглядит следующим образом:
@Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
if (resource == null){
return super.createPropertySource(name, resource);
}
List<PropertySource<?>> sources = new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource());
return sources.get(0);
}
Примечание:spring boot
серединаyml
,yaml
Соответствующий класс нагрузкиYamlPropertySourceLoader
.
контрольная работа
@Component
@PropertySource(value = "test.yml", encoding = "utf-8", factory = TestFactory.class)
@ConfigurationProperties(prefix = "com.test")
public class IdCardServerConfig {
private String serverCode;
...
}