@Value和@PropertySource实现*.properties配置文件读取过程和实现原理
@Value和@PropertySource实现*.properties
配置文件读取过程和实现原理
1 配置使用步骤
(1)右击resource目录添加*.prooerties配置文件
(2)填写配置文件的名称
(3)打开配置文件,填写配置项,按照键值对的形式添加
(4)在main函数的前面采用注解@PropertySource加载配置文件,value表示文件的路径,encoding表示编码格式。注解会自己加载配置文件中的配置项。
(5)在需要使用配置项的地方,加上@Value(”${}”)使用配置项。
2 实现原理
(1)使用@PropertySources加载多个属性文件,@PropertySource对应一个配置文件。value输入文件的路径名称,encoding输入文件的编码方法,如果有汉字,要使用utf-8编码方式。PropertySourceFactory用来加载文件中中的值。
@PropertySources({ @PropertySource(value = "classpath:imp.properties", encoding = "utf-8"),@PropertySource(value = "classpath:kafka.properties", encoding = "utf-8") })
@ImportResource("classpath:spring-dubbo.xml")
public class RedshieldApplication{
public static void main(String[] args) {
SpringApplication.run(RedshieldApplication.class, args);
}
}
(2)注解PropertySources的定义内部是一个数组PropertySource,每个PropertySource对应一个配置文件。
public @interface PropertySources {
PropertySource [] value();
}
(3)PropertySource接口的定义如下,表示配置文件的名称,如果没有输入,会自动生成。value表示文件的路径,用于加载的文件路径。encoding编码格式。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(PropertySources.class)
public @interface PropertySource {
/**
* Indicate the name of this property source. If omitted, a name will
* be generated based on the description of the underlying resource.
* @see org.springframework.core.env.PropertySource#getName()
* @see org.springframework.core.io.Resource#getDescription()
*/
String name() default "";
/**
* Indicate the resource location(s) of the properties file to be loaded.
* <p>Both traditional and XML-based properties file formats are supported
* — for example, {@code "classpath:/com/myco/app.properties"}
* or {@code "file:/path/to/file.xml"}.
* <p>Resource location wildcards (e.g. **/*.properties) are not permitted;
* each location must evaluate to exactly one {@code .properties} resource.
* <p>${...} placeholders will be resolved against any/all property sources already
* registered with the {@code Environment}. See {@linkplain PropertySource above}
* for examples.
* <p>Each location will be added to the enclosing {@code Environment} as its own
* property source, and in the order declared.
*/
String[] value();
/**
* Indicate if failure to find the a {@link #value() property resource} should be
* ignored.
* <p>{@code true} is appropriate if the properties file is completely optional.
* Default is {@code false}.
* @since 4.0
*/
boolean ignoreResourceNotFound() default false;
/**
* A specific character encoding for the given resources, e.g. "UTF-8".
* @since 4.3
*/
String encoding() default "";
/**
* Specify a custom {@link PropertySourceFactory}, if any.
* <p>By default, a default factory for standard resource files will be used.
* @since 4.3
* @see org.springframework.core.io.support.DefaultPropertySourceFactory
* @see org.springframework.core.io.support.ResourcePropertySource
*/
Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;
}
(4)DefaultPropertySourceFactory 属性工厂的实现类,
public class DefaultPropertySourceFactory implements PropertySourceFactory {
@Override
public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException {
return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));
}
}
(5)主要的是ResourcePropertySource类,它的构造函数如下
/**
* Create a PropertySource having the given name based on Properties
* loaded from the given encoded resource.
*/
public ResourcePropertySource(String name, EncodedResource resource) throws IOException {
super(name, PropertiesLoaderUtils.loadProperties(resource));
this.resourceName = getNameForResource(resource.getResource());
}
(6)构造函数中用了PropertiesLoaderUtils.loadProperties函数加载属性
/**
* Load properties from the given EncodedResource,
* potentially defining a specific encoding for the properties file.
* @see #fillProperties(java.util.Properties, EncodedResource)
*/
public static Properties loadProperties(EncodedResource resource) throws IOException {
Properties props = new Properties();
fillProperties(props, resource);
return props;
}
(7)函数内部用了fillProperties函数加载属性。函数实现如下
public static void fillProperties(Properties props, EncodedResource resource)
throws IOException {
fillProperties(props, resource, new DefaultPropertiesPersister());
}
(8)再调用重载函数fillProperties,实现如下,可以看到可以加载xml格式的文件或者Properties格式的配置文件。
/**
* Actually load properties from the given EncodedResource into the given Properties instance.
* @param props the Properties instance to load into
* @param resource the resource to load from
* @param persister the PropertiesPersister to use
* @throws IOException in case of I/O errors
*/
static void fillProperties(Properties props, EncodedResource resource, PropertiesPersister persister)
throws IOException {
InputStream stream = null;
Reader reader = null;
try {
String filename = resource.getResource().getFilename();
if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {//xml格式
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();
}
}
}
(9)加载文件内容实际是PropertiesPersister persister. load函数,看看PropertiesPersister接口的实现类的定义,上面使用的是下面标红的两个函数load和loadFromXml。
public class DefaultPropertiesPersister implements PropertiesPersister {
@Override
public void load(Properties props, InputStream is) throws IOException {
props.load(is);
}
@Override
public void load(Properties props, Reader reader) throws IOException {
props.load(reader);
}
@Override
public void store(Properties props, OutputStream os, String header) throws IOException {
props.store(os, header);
}
@Override
public void store(Properties props, Writer writer, String header) throws IOException {
props.store(writer, header);
}
@Override
public void loadFromXml(Properties props, InputStream is) throws IOException {
props.loadFromXML(is);
}
@Override
public void storeToXml(Properties props, OutputStream os, String header) throws IOException {
props.storeToXML(os, header);
}
@Override
public void storeToXml(Properties props, OutputStream os, String header, String encoding) throws IOException {
props.storeToXML(os, header, encoding);
}
}
(10)看看load函数的实现如下,实际上调用了laod0函数
public synchronized void load(Reader reader) throws IOException {
load0(new LineReader(reader));
}
(11)load0函数的实现如下,绕了一大圈,终于绕到了正题上。这里才是对配置文件进行逐行解析。
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;
while (keyLen < limit) {//获取属性名称
c = lr.lineBuf[keyLen];
//need check if escaped.
if ((c == '=' || c == ':')// 判断属性名称结束&& !precedingBackslash) {
valueStart = keyLen + 1;//开始解析属性值
hasSep = true;
break;
} else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) {
valueStart = keyLen + 1;
break;
}
if (c == '\\') {
precedingBackslash = !precedingBackslash;
} else {
precedingBackslash = false;
}
keyLen++;
}
while (valueStart < limit) {//开始解析属性值,排除空格等字符
c = lr.lineBuf[valueStart];
if (c != ' ' && c != '\t' && c != '\f') {
if (!hasSep && (c == '=' || c == ':')) {
hasSep = true;
} else {
break;
}
}
valueStart++;
}
String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);//获取键
String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);//获取值
put(key, value);//将键和值加入table中
}
}
(12)解析完之后,放入一个hashtable中。并且保证唯一性
/**
* Maps the specified <code>key</code> to the specified
* <code>value</code> in this hashtable. Neither the key nor the
* value can be <code>null</code>. <p>
*
* The value can be retrieved by calling the <code>get</code> method
* with a key that is equal to the original key.
*
* @param key the hashtable key
* @param value the value
* @return the previous value of the specified key in this hashtable,
* or <code>null</code> if it did not have one
* @exception NullPointerException if the key or value is
* <code>null</code>
* @see Object#equals(Object)
* @see #get(Object)
*/
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
addEntry(hash, key, value, index);
return null;
}
(13)结尾的addEntry才是真正的new一个实例,加入table中private transient Entry<?,?>[] table;
private void addEntry(int hash, K key, V value, int index) {
modCount++;
Entry<?,?> tab[] = table;
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();
tab = table;
hash = key.hashCode();
index = (hash & 0x7FFFFFFF) % tab.length;
}
// Creates the new entry.
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
}
(14)最后使用注解@Value获取属性值
@Value("${infosight.bigData.url}")去怎么获取属性值,这个就不得而知了。可能是java内部的bean加载和装配机制,有大神帮忙解惑?
自己开发了一个股票智能分析软件,功能很强大,需要的点击下面的链接获取:
https://www.cnblogs.com/bclshuai/p/11380657.html
百度云盘下载地址:
链接:https://pan.baidu.com/s/1swkQzCIKI3g3ObcebgpIDg
提取码:mc8l
微信公众号获取最新的软件和视频介绍
QStockView


浙公网安备 33010602011771号