@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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix