常用作配置文件的properties
在实际开发过程中,对于系统中经常使用的一些基本不会改变的内容,如访问外部系统的url地址、数据库连接配置等等。通常有以下几种方式存储:
1、存储在配置文件中:编码加载配置文件;配置中心Apollo
2、存储在数据库中
配置文件一般使用properties文件,下面就简单的使用jdk自带Properties的api,在src/properties目录下新建配置文件db.properties。
一、db.properties的配置文件
1 driverClass = com.mysql.cj.jdbc.Driver 2 jdbcUrl = jdbc:mysql://localhost:3306/test 3 user = root 4 password = root
二、读取配置文件信息
1 import java.io.BufferedInputStream; 2 import java.io.FileInputStream; 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.util.Properties; 6 7 public class PropertiesDemo { 8 public static void main(String[] args) { 9 InputStream in = null; 10 try { 11 // ClassLoader加载配置文件 12 in = PropertiesDemo.class.getClass().getResourceAsStream("/properties/db.properties"); 13 // 通过文件流读取 14 // in = new BufferedInputStream(new FileInputStream("src/properties/db.properties")); 15 Properties prop = new Properties(); 16 try { 17 prop.load(in); 18 } catch (IOException e) { 19 e.printStackTrace(); 20 } 21 String driverClass = prop.getProperty("driverClass"); 22 String jdbcUrl = prop.getProperty("jdbcUrl"); 23 String user = prop.getProperty("user"); 24 String password = prop.getProperty("password"); 25 System.out.println("数据库驱动: " + driverClass + "; \n数据库url: " + jdbcUrl + "; \n数据库用户: " + user + "; \n数据库密码: " + password); 26 }catch (Exception e1) { 27 e1.printStackTrace(); 28 }finally { 29 // 关闭流 30 if(in != null) { 31 try { 32 in.close(); 33 } catch (IOException e) { 34 e.printStackTrace(); 35 } 36 } 37 } 38 } 39 }
三、运行结果
四、Properties源码分析
1、Properties类的继承自Hashtable。
方法setProperty(String, String)、getProperties(String)就是利用Hashtable的put/get方法实现的。
public class Properties extends Hashtable<Object,Object>{}
2、Properties的构造方法
/** * 创建一个没有指定默认属性的Properties */ public Properties() { this(null); } /** * 创建一个有指定默认属性的Properties */ public Properties(Properties defaults) { this.defaults = defaults; }
3、Properties的load(InputStream in)方法
Properties所有加载流的方法都是都调用了load0(LineReader lr)方法。
3.1、内部类LineReader
LineReader是Properties的一个内部类,作用是过滤properties文件中的注释、空格等内容,并将过滤后的内容存储在内存中,
即lineBuf[]字符数组中。下面看一下LineReader的方法,支持输入流、字符流的构造函数。
3.2、load0(LineReader lr)方法
private void load0 (LineReader lr) throws IOException { // 转换的字符数组 char[] convtBuf = new char[1024]; // 读取当前行的长度 int limit; // key的长度 int keyLen; // value的开始位置 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; // 读取key的内容 while (keyLen < limit) { // 获取key中的字符 c = lr.lineBuf[keyLen]; //need check if escaped. // 此处解释了properties文件中key、value的书写方式:key=value、key:value,并且判断前一个字符不是转义字符 if ((c == '=' || c == ':') && !precedingBackslash) { valueStart = keyLen + 1; // 设置连接符处理标识 hasSep = true; break; // tab方式、空格方式的key、value也可被读取 } else if ((c == ' ' || c == '\t' || c == '\f') && !precedingBackslash) { valueStart = keyLen + 1; break; } // 当前读取的字符为'\\',更新是否是转义字符标识 if (c == '\\') { precedingBackslash = !precedingBackslash; } else { precedingBackslash = false; } keyLen++; } // 读取value的内容 while (valueStart < limit) { c = lr.lineBuf[valueStart]; if (c != ' ' && c != '\t' && c != '\f') { if (!hasSep && (c == '=' || c == ':')) { hasSep = true; } else { break; } } valueStart++; } // 将字符数组中的key、value转化为字符串 String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf); String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf); // Hashtable的put方法,将key、value存储在内存中 put(key, value); } }
load0()方法作用是读取在LineReader中得到的字符数组,由特定的连接符(如’=’、’:’)为标记位获得key、value的长度,通过key、value的长度获取字符数组中的对应的内容并转为字符串,将键值对通过Hashtable的put方法存储在内存中。
4、Properties的getProperties(String)方法
通过HashTable的get方法获取指定key的内容,若得到的结果不是String类型的,并且在初始化Properties时没有自定义的Properties内容,则返回null。否则读取自定义Properties中的内容并返回。
5、Properties的setProperties(String, String)方法
调用Hashtable的put方法,将指定key、value设置进内存中。此时的新加的key、value并未保存在properties文件中,若需要将内荣保存在properties中还需要调用store方法。
6、Properties的store(Writer, String)方法
store(...)方法调用store0()方法,代码如下
private void store0(BufferedWriter bw, String comments, boolean escUnicode) throws IOException { // 添加评论 if (comments != null) { writeComments(bw, comments); } // 添加文件创建日期 bw.write("#" + new Date().toString()); // 换行 bw.newLine(); synchronized (this) { // 遍历键key for (Enumeration<?> e = keys(); e.hasMoreElements();) { // 获取键key String key = (String)e.nextElement(); // 获取值value String val = (String)get(key); // 获取key的单个字符做处理后,用StringBuffer拼接返回处理后的key key = saveConvert(key, true, escUnicode); // 获取value的单个字符做处理后,用StringBuffer拼接返回处理后的value val = saveConvert(val, false, escUnicode); // 将key、value 用’=’连接写入 bw.write(key + "=" + val); // 换行 bw.newLine(); } } // 刷新写入 bw.flush(); }
store0方法的作用将内存中Hashtable的数据通过指定编码持久化到指定的文件中,key、value统一用’=’做连接符。
下面是一个用store方法的简单demo。
import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.util.Properties; public class PropertiesDemo { public static void main(String[] args) { InputStream in = null; try { // ClassLoader加载配置文件 in = PropertiesDemo.class.getClass().getResourceAsStream("/properties/db.properties"); Properties prop = new Properties(); try { prop.load(in); } catch (IOException e) { e.printStackTrace(); } // setProperty将test键值对通过Hashtable保存在内存中 prop.setProperty("test", "test"); System.out.println(prop); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File("src/properties/db1.properties")))); // 将内存中的键值对持久化到db1.properties文件中 prop.store(bw, "add key-test, value-test"); bw.close(); }catch (Exception e1) { e1.printStackTrace(); }finally { // 关闭流 if(in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
db1.properties的内容如下,新加的内容test已经保存在文件中。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)