The handwritten distributed configuration center is in progress step by step.
These days, Spring Boot is basically used for development, and everyone knows that there will be a application.properties
configuration file in the project (some are also application.yaml
, anyway, it is used to save some of our configuration information), usually we will write some configuration information into the properties file, such as: database connection information, third-party interface information (key, user name, password, address, etc.) , connection pool, Redis configuration information, various third-party component configuration information, etc.
In single services, or even in some small distributed architectures, project configuration relies on a application.properties
configuration file (some projects may have an environment distinction, For example: application-dev.properties
, application-pro.properties
, etc.). However, as the business develops and the architecture continues to upgrade, there will be more and more service data and configuration information involved in each service, and the requirements for configuration management will also become higher and higher, such as real-time and independent configuration information. Sex etc.
At the same time, under the microservice architecture, we may also involve configuration management, grayscale publishing, dynamic current limiting, dynamic downgrade and other requirements in different environments, including the security and permissions of configuration content, so Traditional configuration and maintenance methods are difficult to meet the needs.
So, the distributed configuration center was born in such an environment.
In this article, we first figure out what methods are there to read the properties configuration file in java.
The requirement is that there is a jdbc.properties
configuration file in our project, which is as follows:
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=mysql://localhost:3306/database?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=123456
Now we want to get the content of the above configuration file in the java code .
The first way we use: this.getClass().getResourceAsStream()
Properties
Code implementation:
/** * @author tianwc 公众号:java后端技术全栈、面试专栏 * @version 1.0.0 * @date 2023年05月27日 09:13 * 在线刷题1200+,100+篇干货文章:<a href="http://woaijava.cc/">博客地址</a> */ public void readProperties1() throws IOException { //不加/会从当前包进行寻找,加上/会从src开始找 InputStream inputStream = this.getClass().getResourceAsStream("/jdbc.properties"); Properties properties=new Properties(); properties.load(inputStream); System.out.println("jdbc.driver="+properties.getProperty("jdbc.driver")); System.out.println("jdbc.url="+properties.getProperty("jdbc.url")); System.out.println("jdbc.username="+properties.getProperty("jdbc.username")); System.out.println("jdbc.password="+properties.getProperty("jdbc.password")); }
Let’s talk about the above code:
this. getClass().getResourceAsStream()
The specific file and code location is, the code is in the
src/main/java
directory, the resource The file is in thesrc/main/resources/
directory.will be searched from the directory of the current class. If this file is not in the same directory as the class, it will not be found.
will be found from the entire compiled classes directory. Maven will also package the resource files into the classes folder, so it can be found.
ClassLoader
is found from the entire classes folder, so there is no need to add/
Properties: java.util.Properties, this class is mainly used to read Java configuration files, different programming languages have their own support Configuration file, many variables in the configuration file are frequently changed. In order to facilitate user configuration, it allows users to modify related variable settings without leaving the program itself. Just like in Java, its configuration file is often a
.properties file, which configures parameters in the form of key-value pairs.
Hashtable, I believe everyone knows
Hashtable is a key-value data structure class that corresponds to the content of our properties file, which is also in key-value form.
getProperty(String key) : Search this property list for a property with the specified key . If the key is not found in this property list, the default property list and its default value are checked (recursively). If the property is not found, the method returns the default value parameter.
list(PrintStream out) Print this property list to the specified output stream. This method is useful for debugging.
load(InputStream inStream)
: Read the attribute list (key and element pairs) from the input byte stream. The input stream is in the simple line-oriented format specified in the load (Reader), and assumes the ISO 8859-1
character encoding; that is, each byte is a Latin1 character. Characters not in Latin1 and certain special characters are represented in keys and elements using Unicode escapes. After this method returns, the specified stream remains open.
setProperty(String key, String value)
: Call the Hashtable method put. He sets the key-value pairs by calling the put method of the base class.
store(OutputStream out, String comments)
: This list of properties (key and element pairs) from this Properties table in a format suitable for loading into the Properties table using the load(InputStream) method Write to the output stream. This Properties method does not write out the properties (if any) in the defaults table of this Properties table.
storeToXML(OutputStream os, String comment, String encoding)
: Emits an XML document representing all attributes contained in this table using the specified encoding.
clear()
: Clear this hash table so that it does not contain any keys.
stringPropertyNames()
: Returns a set of keys in this property list, where the keys and their corresponding values are strings, if a key with the same name has not been found from the main property list, then include different keys from the default property list. Keys or properties whose keys are not of type String will be omitted.
properties.load(inputStream)
public synchronized void load(InputStream inStream) throws IOException { load0(new LineReader(inStream)); } 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); } }
最后调用put(key, value);
这个put方法就是Hashtable
中的put方法。这里可以这么理解:将我们的配置项保存到Hashtable
中。
getProperty(String key)
public String getProperty(String key) { Object oval = super.get(key); String sval = (oval instanceof String) ? (String)oval : null; return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval; }
super.get(key);
就是调用Hashtable
中的get()
方法,也就是此时返回value,同时这就对应返回了properties文件中key对应的value。
第二种方式,我们通过当前类的加载器进行读取this.getClass().getClassLoader().getResourceAsStream()
获取InputStream。
代码实现:
/** * @author tianwc 公众号:java后端技术全栈、面试专栏 * @version 1.0.0 * @date 2023年05月27日 09:13 * 博客地址:<a href="http://woaijava.cc/">在线刷题1200+,100+篇干货文章</a> */ public void readProperties2() throws IOException { //不加/,若加了会为null InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("jdbc.properties"); //如果放在config目录下 //InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("config/jdbc.properties"); Properties properties=new Properties(); properties.load(inputStream); System.out.println("jdbc.driver="+properties.getProperty("jdbc.driver")); System.out.println("jdbc.url="+properties.getProperty("jdbc.url")); System.out.println("jdbc.username="+properties.getProperty("jdbc.username")); System.out.println("jdbc.password="+properties.getProperty("jdbc.password")); }
第一看怎么觉得和第一种方式很像,下面来说说两个的区别。
this.getClass.getResourceAsStream()
Find the configuration file location starting from the location of the current class. To find jdbc.properties
you must add /
start from classpath
and search By default, the search starts from the
classpath path, and adding
/ will report a
null pointer exception. The rest of the code is the same as the first method, so I won’t go into details here.
ClassLoader class static method
getSystemResourceAsStream().
/** * @author tianwc 公众号:java后端技术全栈、面试专栏 * @version 1.0.0 * @date 2023年05月27日 09:13 * 博客地址:<a href="http://woaijava.cc/">在线刷题1200+,100+篇干货文章</a> */ public void readProperties3() throws IOException { //如果存放到config目录下 //InputStream inputStream = ClassLoader.getSystemResourceAsStream("config/jdbc.properties"); InputStream inputStream = ClassLoader.getSystemResourceAsStream("jdbc.properties"); Properties properties=new Properties(); properties.load(inputStream); System.out.println("jdbc.driver="+properties.getProperty("jdbc.driver")); System.out.println("jdbc.url="+properties.getProperty("jdbc.url")); System.out.println("jdbc.username="+properties.getProperty("jdbc.username")); System.out.println("jdbc.password="+properties.getProperty("jdbc.password")); }
ClassLoader中的
getSystemResourceAsStream()
方法,它用于获取资源作为参数并将资源转换为InputStream
。例如,我们可以使用该方法获取网站的静态资源并将其转换为InputStream
。
说白了就是获取InputStream
的方式不同罢了,最终还是交给Properties去解析jdbc.properties
文件内容。
我们在实际开发中,基本上都是离不开Spring了,所以,接下来我们使用Spring中的 ClassPathResource
读取配置文件。
代码实现:
/** * @author tianwc 公众号:java后端技术全栈、面试专栏 * @version 1.0.0 * @date 2023年05月27日 09:13 * 博客地址:<a href="http://woaijava.cc/">博客地址</a> */ public void readProperties4() throws IOException { ClassPathResource resource = new ClassPathResource("jdbc.properties"); //ClassPathResource resource = new ClassPathResource("config/jdbc.properties"); Properties properties= PropertiesLoaderUtils.loadProperties(resource); System.out.println("jdbc.driver="+properties.getProperty("jdbc.driver")); System.out.println("jdbc.url="+properties.getProperty("jdbc.url")); System.out.println("jdbc.username="+properties.getProperty("jdbc.username")); System.out.println("jdbc.password="+properties.getProperty("jdbc.password")); }
这里PropertiesLoaderUtils
是spring-core.jar下面的,全路径名称:
org.springframework.core.io.support.PropertiesLoaderUtils
。
PropertiesLoaderUtils.loadProperties(resource)
源码部分:
public static Properties loadProperties(EncodedResource resource) throws IOException { //创建一个Properties对象 Properties props = new Properties(); //处理文件内容并赋值给props fillProperties(props, resource); return props; }
fillProperties(props, resource);
方法:
public static void fillProperties(Properties props, EncodedResource resource) throws IOException { fillProperties(props, resource, ResourcePropertiesPersister.INSTANCE); } static void fillProperties(Properties props, EncodedResource resource, PropertiesPersister persister) throws IOException { InputStream stream = null; Reader reader = null; try { //省略不相关代码 stream = resource.getInputStream(); //获取InputStream persister.load(props, stream); } finally { //关闭 } }
最后到PropertiesPersister
的persister.load(props, stream);
public void load(Properties props, InputStream is) throws IOException { props.load(is); }
这里又回到Properties类中的load()方法里了。
绕了半天也只是获取
InputStream
的方式不同而已
接下来我们来使用PropertyResourceBundle
读取InputStream
流,实现配置文件读取。
代码实现:
public void readProperties5() throws IOException { InputStream inputStream = ClassLoader.getSystemResourceAsStream("jdbc.properties"); //InputStream inputStream = ClassLoader.getSystemResourceAsStream("config/jdbc.properties"); PropertyResourceBundle bundle = new PropertyResourceBundle(inputStream); System.out.println(bundle.getString("jdbc.driver")); System.out.println(bundle.getString("jdbc.url")); System.out.println(bundle.getString("jdbc.username")); System.out.println(bundle.getString("jdbc.password")); }
好像也没什么,
PropertyResourceBundle
源码我们来看看 new PropertyResourceBundle(inputStream);
源码部分:
public PropertyResourceBundle (InputStream stream) throws IOException { Properties properties = new Properties(); properties.load(stream); lookup = new HashMap(properties); }
这个构造方法里直接new了一个Properties对象。然后调用load方法解析。
所以,这种方式无非就是在Properties基础之上再封装了,也就是让我们使用起来更加方便。
所以,上面代码中的bundle.getString("jdbc.url")
其实调用的是父类中方法;
public final String getString(String key) { return (String) getObject(key); }
最终调用到PropertyResourceBundle
的handleGetObject()
方法:
public Object handleGetObject(String key) { if (key == null) { throw new NullPointerException(); } return lookup.get(key); }
lookup就是一个HashMap:lookup = new HashMap(properties);
第五种方式中我们看到了ResourceBundle
,接下来我们就是用ResourceBundle.getBundle()
实现。
//不用输入后缀 public void readProperties6() { ResourceBundle bundle=ResourceBundle.getBundle("jdbc"); System.out.println(bundle.getString("jdbc.driver")); System.out.println(bundle.getString("jdbc.url")); System.out.println(bundle.getString("jdbc.username")); System.out.println(bundle.getString("jdbc.password")); }
直接使用文件名称就可以了,不需要写文件后缀名。
java.util.ResourceBundle.getBundle(String baseName)
方法获取使用指定的基本名称,不需要文件后缀名,默认的语言环境和调用者的类加载器获取资源包。
If baseName
is null, an exception will be reportedNullPointerException
If the specified base can be found and no corresponding resource package, an exception MissingResourceException
The above is the detailed content of 6 ways to read properties files, recommended to collect!. For more information, please follow other related articles on the PHP Chinese website!