@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 
    * &mdash; for example, {@code "classpath:/com/myco/app.properties"} 
    * or {@code "file:/path/to/file.xml"}. 
    * <p>Resource location wildcards (e.g. *&#42;/*.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

猜你喜欢

转载自www.cnblogs.com/bclshuai/p/11437935.html
今日推荐