配置文件的几种读取方式(Java和Lua)
程序员文章站
2022-06-02 13:53:13
...
目录
前言
在工作中为了方便项目管理,通常会用到配置文件,以前用的都是配置excel表格转成json格式文件,再读取数据,记录一些有用的方法,也提供给大家参考
Java读取properties配置文件
这种解析方式就轻便很多,适用于配置文件数据小的场景
配置文件数据
读取文件方式
@Override
protected void register() {
Properties p = PropertiesUtils.read("platform/huaweih5.properties");
APP_ID = PropertiesUtils.getString(p, "appid");
USER_ID = PropertiesUtils.getString(p, "userID");
PAY_PUBLIC_KEY = PropertiesUtils.getString(p, "payPublicKey");
PAY_PRIVATE_KEY = PropertiesUtils.getString(p, "payPrivateKey");
PUBLIC_KEY = PropertiesUtils.getString(p, "publicKey");
context.put(CHANNEL_NAME, this);
}
解析方式
/**
* Properties帮助类
* @author CharonWang
*
*/
public class PropertiesUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(PropertiesUtils.class);
/**
* 读取properties文件
* @param filePath
* @return
*/
public static Properties read(String filePath) {
Properties properties = new Properties();
InputStream is = PropertiesUtils.class.getClassLoader().getResourceAsStream(filePath);
if (is == null) {
LOGGER.error(String.format("[%s] file path not found.", filePath));
return null;
}
try {
properties.load(is);
is.close();
} catch (IOException e) {
LOGGER.error(String.format("[%s] file load error.", filePath));
return null;
} finally {
}
return properties;
}
/**
* 获取整型值
* @param p
* @param name
* @return
*/
public static int getInt(Properties p, String name) {
String str = p.getProperty(name).trim();
if (str == null || str.isEmpty()) {
return 0;
}
return Integer.valueOf(p.getProperty(name).trim());
}
/**
* @param p
* @param name
* @return
*/
public static long getLong(Properties p, String name) {
String str = p.getProperty(name).trim();
if (str == null || str.isEmpty()) {
return 0;
}
return Long.valueOf(p.getProperty(name).trim());
}
/**
* 获取字符串值
* @param p
* @param name
* @return
*/
public static String getString(Properties p, String name) {
return p.getProperty(name).trim();
}
/**
* 逗号分隔获取字符串List
* @param p
* @param name
* @return
*/
public static List<String> dotSplitStringList(Properties p, String name) {
return getSplitList(p, name, ",");
}
/**
* 逗号分隔获取整型List
* @param p
* @param name
* @return
*/
public static List<Integer> dotSplitIntList(Properties p, String name) {
return getSplitList(p, name, ",");
}
public static List<Long> dotSplitLongList(Properties p, String name) {
return getSplitList(p, name, ",");
}
@SuppressWarnings("unchecked")
public static <T> List<T> getSplitList(Properties p, String name, String splitChar) {
if (name == null || name.isEmpty()) {
return new ArrayList<>();
}
String splitString = getString(p, name);
if (splitString == null || splitString.isEmpty()) {
return new ArrayList<>();
}
String[] splitArray = splitString.split(splitChar.trim());
List<T> list = new ArrayList<>();
for (String s : splitArray) {
if (StringUtils.isNotBlank(s)) {
list.add((T) s.trim());
}
}
return list;
}
}
Lua读取config配置文件
先是excel表格转config文件,再读取数据
配置文件数据
配置文件的读取
实例中通过主键id读取数据
-- 领取奖励
function Achievement:GetReward(achievementid)
local achievementconfig = server.configCenter.AchievementConfig[achievementid]
if achievementconfig then
if achievementconfig.drop then
local rewards = server.dropCenter:DropGroup(achievementconfig.drop)
if rewards then
self.player:GiveItem(rewards, 1, 1,server.baseConfig.YuanbaoRecordType.Achievement)
end
end
end
if achievementconfig.title then
self.role.titleeffect:ActivatePart({id = achievementconfig.title,time = 0})
end
end
读取文件
将配置信息放入server.configCenter中,元表的骚操作是真的多,
function config:ShareConfig()
server.configCenter = {}
local mt = {}
mt.__index = function(_,key)
local cfg = lua_share.query(key)
if cfg == nil then
lua_app.log_error("config query failed",key)
return {}
end
return cfg
end
mt.__newindex = function(_,key,value)
lua_app.log_info("error config new")
end
setmetatable(server.configCenter,mt)
end
解析文件
lua_share方法
local function service()
if handler == 0 then
handler = lua_app.unique_lua("share/share_svc")
end
return handler
end
function sharedata.query(name,call)
if cache[name] then
return cache[name]
end
local obj = lua_app.call(service(), "lua", "query", name)
local r = sd.box(obj)
lua_app.send(service(), "lua", "confirm" , obj)
lua_app.fork(monitor,name, r, obj,call)
cache[name] = r
return r
end
lua_app方法
function unique_lua(name,...)
local handle = clib.unpack(raw_call(".launcher","lua",clib.pack("unique_process","LuaProxy",name,...)))
if type(handle) == "number" then
return handle
end
return 0
end
function call(addr,type_name,...)
local protocol = protocols[type_name]
if watching_process[addr] == false then
error("Service is dead")
end
local session = clib.new_session()
local ret = clib.send(addr,session,protocol.id,protocol.pack(...))
if ret == nil or ret == -1 then
error("call to invalid address "..tostring(addr))
end
return protocol.unpack(yield_call(addr,session))
end
function send(dest,type_name,...)
local protocol = protocols[type_name]
if watching_process[dest] == false then
error("Service is dead")
end
return clib.send(dest,0,protocols[type_name].id,protocol.pack(...))
end
function fork(func,...)
local args = {...}
local co = new_co(function()
func(table.unpack(args))
end)
table.insert(fork_queue,co)
return co
end
引用的clib应该是C++文件
share_core方法
function conf.box(obj)
local gcobj = core.box(obj)
return setmetatable({
__parent = false,
__obj = obj,
__gcobj = gcobj,
__key = "",
} , meta)
end
Java读取json配置文件
先是excel表格转json文件,再读取数据,每次需要新建一个对应配置类,功能实现比较复杂,节省篇幅删减部分字段
配置文件数据
fileName对应配置文件名称,新建配置类对应文件字段
@DataFile(fileName = "seize_mine_config")
public class Sei*eConfig implements ModelAdapter {
//矿ID
private int id;
// 占领箱子
private String boxReward;
//消耗
private String seizeConsume;
@FieldIgnore
private List<CountProduct> seizeConsumes;
@Override
public void initialize() {
//字符串转物品类 id count
seizeConsumes = CountProduct.create(seizeConsume);
}
@Override
public IdentiyKey findKey() {
return IdentiyKey.build(id);
}
public int getId() {
return id;
}
public List<CountProduct> getSeizeConsumes() {
return seizeConsumes;
}
}
/**
* model包中的类继承于此
* resource.dataconfig包中的文件名必需和model类名一样(忽略大小写)
* @author ludd
*/
public interface ModelAdapter {
/**
* 初始化处理方法(用于model初始化时做一些自定义处理)
*/
public void initialize();
public IdentiyKey findKey();
}
配置文件的读取
实例中通过主键id读取数据
// 验证消耗
Sei*eConfig mineConfig = dataConfig.getConfig(IdentiyKey.build(mineId), Sei*eConfig.class);
List<CountProduct> seizeConsumes = mineConfig.getSeizeConsumes();
读取接口
/**
* 数据配置接口
* @author ludd
*
*/
public interface DataConfig {
/**
* 根据类名获取数据配置列表
* @param invokeClazz 调用者的Service类
* @param modelClass 需要获取的Model类
* @return
*/
<T extends ModelAdapter> Collection<T> getList(Class<T> modelClass);
/**
* 根据类名获取数据配置列表
* @param invokeClass
* @param modelClass
* @return
*/
<T extends ModelAdapter> Collection<T> listAll(ConfigServiceAdapter invokeClazz, Class<T> modelClass);
/**
* 重载配置文件
* @param fileName 文件名
* @param newData 新的文件流
* @return
*/
boolean reload(String fileName, URL url) throws Exception;
/**
* 校验配置文件流
* @param name
* @param inputStream
* @return
*/
boolean checkModelAdapter(String name, InputStream inputStream);
/**
* 获取配置文件
* @param key
* @return
*/
<T extends ModelAdapter> T getConfig(IdentiyKey key, Class<T> clz);
}
功能实现
**
* 数据配置接口功能实现
* @author ludd
*
*/
@Component
public class DataConfigImpl implements DataConfig, InitializingBean {
private static final Logger LOGGER = LoggerFactory.getLogger(DataConfigImpl.class);
/**
* 配置文件格式(xml,json)
*/
@Autowired(required = false)
@Qualifier("datacofig.format")
private String format = "json";
/**
* 配置文件路径
*/
@Autowired(required = false)
@Qualifier("dataconfig.path")
private String path = "dataconfig" + File.separator;
/**
* 数据配置映射对应的包
*/
@Qualifier("dataconfig.package_scan")
@Autowired(required = false)
private String packageScan = ".";
/**
* 配置文件扩展名
*/
@Qualifier("dataconfig.extension")
@Autowired(required = false)
private String extension = ".json";
@Autowired
private DataParser dataParser;
@Autowired
private ApplicationContext applicationContext;
/**
* 所有数据配置存储集合 key:className value: extend ModelAdapter
*/
private static ConcurrentHashMap<String, Map<Object, ModelAdapter>> MODEL_MAPS = new ConcurrentHashMap<>();
/** model类被哪些Service调用了的索引(用于反向初始化service) */
private static ConcurrentHashMap<String, Set<ConfigServiceAdapter>> MODEL_BE_INVOKE_MAPS = new ConcurrentHashMap<>();
/**
* model类与名称的映射
* key:DataFile.fileName() value:Class
*/
private static ConcurrentHashMap<String, Class<ModelAdapter>> MODEL_CLASS_MAPS = new ConcurrentHashMap<>();
@Override
public void afterPropertiesSet() throws Exception {
initModelAdapterList();
initServiceAdapterList();
}
@Override
@SuppressWarnings("unchecked")
public <T extends ModelAdapter> Collection<T> getList(Class<T> modelClass) {
String name = modelClass.getName();
Map<Object, ModelAdapter> map = MODEL_MAPS.get(name);
if (map == null) {
throw new RuntimeException(name + " config not found");
}
return (Collection<T>) map.values();
}
@Override
public boolean reload(String fileName, URL url) {
if (fileName.isEmpty() || url == null) {
return false;
}
String filePath = getFullPath(fileName);
URL resource = null;
InputStream inputStream = null;
OutputStream outputStream = null;
try {
resource = getClass().getClassLoader().getResource(filePath);
inputStream = url.openStream();
outputStream = new FileOutputStream(URLDecoder.decode(resource.getPath(), "utf-8"));
byte[] buffer = new byte[1024];
int readed = 0; // 一次读多个,readed代表当前已读的数据总数
while ((readed = inputStream.read(buffer)) != -1) {
// 从第0位写,reader代表读写几位
outputStream.write(buffer, 0, readed);
}
Class<ModelAdapter> clazz = MODEL_CLASS_MAPS.get(fileName);
if (clazz == null) {
return false;
}
// 重载model
if (initModelAdapter(clazz)) {
// 反向重载Service类
Set<ConfigServiceAdapter> serviceClazzSet = MODEL_BE_INVOKE_MAPS.get(fileName);
if (serviceClazzSet != null) {
for (ConfigServiceAdapter service : serviceClazzSet) {
initServiceAdapter(service);
}
}
LOGGER.error(String.format("reload file:[%s]", fileName));
return true;
}
return false;
} catch (Exception e) {
LOGGER.error("{}", e);
} finally {
try {
inputStream.close();
outputStream.close();
} catch (Exception e) {
LOGGER.error("{}", e);
}
}
return false;
}
/**
* 初始化ModelAdapter
*/
private void initModelAdapterList() throws Exception {
String[] temp = packageScan.split(",");
// 通过包名扫描获取对应的类集合
Collection<Class<ModelAdapter>> collection = PackageScanner.scanPackages(temp);
if (collection == null || collection.isEmpty()) {
LOGGER.error(String.format("在 [%s]包下没有扫描到实体类!", packageScan));
return;
}
for (Class<ModelAdapter> clazz : collection) {
initModelAdapter(clazz);
}
LOGGER.info("all data config file load complete!");
}
/**
* 初始化配置
* @param clazz
* @throws Exception
*/
public boolean initModelAdapter(Class<ModelAdapter> clazz) throws Exception {
try {
DataFile df = clazz.getAnnotation(DataFile.class);
if (df == null) {
return false;
}
Map<Object, ModelAdapter> modelAdapterMap = new HashMap<>();
for (String fileName : df.fileName()) {
String fullPath = getFullPath(fileName);
URL resource = getClass().getClassLoader().getResource(fullPath);
if (resource == null) {
LOGGER.error(String.format("load data config file [%s] error. file name [%s] not exists!", clazz.getName(), fullPath));
return false;
}
InputStream input = null;
input = resource.openStream();
Map<Object, ModelAdapter> map = dataParser.parse(input, clazz);
input.close();
if (map.size() < 1) {
return false;
}
for (ModelAdapter obj : map.values()) {
obj.initialize();
}
synchronized (MODEL_CLASS_MAPS) {
MODEL_CLASS_MAPS.put(fileName, clazz);
}
modelAdapterMap.putAll(map);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format("[%s] file load complete!", fullPath));
}
}
synchronized (MODEL_MAPS) {
MODEL_MAPS.put(clazz.getName(), modelAdapterMap);
}
return true;
} catch (Exception e) {
LOGGER.error(String.format("file: [%s] read error!", clazz.getName()), e);
return false;
}
}
/**
* 根据文件名获取全路径
* @param fileName
* @return
*/
private String getFullPath(String fileName) {
return this.path + fileName + this.extension;
}
@SuppressWarnings("unchecked")
@Override
public <T extends ModelAdapter> T getConfig(IdentiyKey key, Class<T> clz) {
String name = clz.getName();
Map<Object, ? extends ModelAdapter> map = MODEL_MAPS.get(name);
if (map == null) {
return null;
}
if (!map.containsKey(key)) {
return null;
}
return (T) map.get(key);
}
}
配置文件的解析
文件解析
/**
* 数据解析接口
*
* @author ludd
*
*/
public interface DataParser {
/**
* 读取配置文件后进行解析
*
* @param stream 文件流
* @param className 解析映射类文件
* @return
*/
public <T extends ModelAdapter> Map<Object,T> parse(InputStream stream, Class<T> className);
}
@Component
public class JsonDataParser implements DataParser {
private static final Logger LOGGER = LoggerFactory.getLogger(JsonDataParser.class);
@Override
public <T extends ModelAdapter> Map<Object, T> parse(InputStream stream, Class<T> className) {
StringBuilder sb = new StringBuilder();
Map<Object, T> objList = new HashMap<Object, T>();
try {
BufferedReader br = new BufferedReader(new InputStreamReader(stream, "utf-8"));
while (true) {
String str = br.readLine();
if (str == null) {
break;
}
sb.append(str);
}
} catch (UnsupportedEncodingException e) {
LOGGER.error("{}", e);
} catch (IOException e) {
LOGGER.error("{}", e);
}
String jsonString = sb.toString();
JSONArray jsonArray = JSON.parseArray(jsonString);
Map<String, Field> fiedlList = getFieldList(className);
for (int i = 0; i < jsonArray.size(); i++) {
JSONObject jsonObj = jsonArray.getJSONObject(i);
T t;
try {
t = className.newInstance();
} catch (InstantiationException e) {
LOGGER.error("{}", e);
continue;
} catch (IllegalAccessException e) {
LOGGER.error("{}", e);
continue;
}
Set<String> keySet = jsonObj.keySet();
for (String string : keySet) {
if (!jsonObj.containsKey(string)) {
LOGGER.warn(String.format("[%s]->[%s] column not exists in datafile!", className.getName(), string));
continue;
}
if (fiedlList.containsKey(string) && jsonObj.containsKey(string)) {
Field f = fiedlList.get(string);
f.setAccessible(true);
Object value = null;
if (f.getType() == byte.class || f.getType() == Byte.class) {
try {
value = jsonObj.getByteValue(string);
} catch (Exception e) {
LOGGER.warn(String.format("[%s]->[%s] column not data null!", className.getName(), string));
}
} else if (f.getType() == short.class || f.getType() == Short.class) {
try {
value = jsonObj.getShortValue(string);
} catch (Exception e) {
LOGGER.warn(String.format("[%s]->[%s] column not data null!", className.getName(), string));
}
} else if (f.getType() == int.class || f.getType() == Integer.class) {
try {
value = jsonObj.getIntValue(string);
} catch (Exception e) {
LOGGER.warn(String.format("[%s]->[%s] column not data null!", className.getName(), string));
}
} else if (f.getType() == long.class || f.getType() == Long.class) {
try {
value = jsonObj.getLongValue(string);
} catch (Exception e) {
LOGGER.warn(String.format("[%s]->[%s] column not data null!", className.getName(), string));
}
} else if (f.getType() == float.class || f.getType() == Float.class) {
try {
value = jsonObj.getFloatValue(string);
} catch (Exception e) {
LOGGER.warn(String.format("[%s]->[%s] column not data null!", className.getName(), string));
}
} else if (f.getType() == double.class || f.getType() == Double.class) {
try {
value = jsonObj.getDoubleValue(string);
} catch (Exception e) {
LOGGER.warn(String.format("[%s]->[%s] column not data null!", className.getName(), string));
}
} else if (f.getType() == String.class) {
try {
value = jsonObj.getString(string);
} catch (Exception e) {
LOGGER.warn(String.format("[%s]->[%s] column not data null!", className.getName(), string));
}
} else if (f.getType() == Boolean.class || f.getType() == boolean.class) {
try {
value = jsonObj.getBoolean(string);
} catch (Exception e) {
LOGGER.warn(String.format("[%s]->[%s] column not data null!", className.getName(), string));
}
} else {
LOGGER.warn(String.format("[%s]->[%s] column not support type!", className.getName(), string));
}
if (value != null) {
try {
f.set(t, value);
} catch (IllegalArgumentException e) {
LOGGER.error("{}", e);
} catch (IllegalAccessException e) {
LOGGER.error("{}", e);
}
}
} else {
LOGGER.warn(String.format("[%s]->[%s] column not exists in class!", className.getName(), string));
}
}
if (t.findKey() == null) {
throw new RuntimeException(String.format("null config key::%s, class:%s", t.findKey(), className));
}
if (objList.containsKey(t.findKey())) {
throw new RuntimeException(String.format("duplicate config key:%s, class:%s", t.findKey().toString(), className));
}
objList.put(t.findKey(), t);
}
return objList;
}
private static Map<String, Field> getFieldList(Class<?> clazz) {
Map<String, Field> fieldList = new HashMap<>();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
if (field.getName().equals("serialVersionUID") || field.isAnnotationPresent(FieldIgnore.class) == false) {
fieldList.put(field.getName(), field);
}
}
return fieldList;
}
}
热更配置文件
自动扫描newconfig路径下是否存在配置文件,如果存在则调用reloadConfig方法
实现方法
@Component
public class ReloadConfig implements InitializingBean {
private static final Logger LOGGER = LoggerFactory.getLogger(ReloadConfig.class);
/**
* 配置文件路径
*/
@Autowired(required = false)
@Qualifier("dataconfig.newconfig")
private String path = "newconfig" + File.separator;
/**
* 配置文件扩展名
*/
@Autowired(required = false)
@Qualifier("dataconfig.extension")
private String extension = ".xml";
/**
* 扫描配置文件变更间隔(毫秒)
*/
@Autowired(required = false)
@Qualifier("dataconfig.flush_time")
private long flushTime = 10000l;
/**
* 热刷备份文件扩展名
*/
private static final String bakExtension = ".bak";
@Autowired
DataConfig dataConfig;
private boolean isRun = true;
@Autowired
Schedule schedule;
@Override
public void afterPropertiesSet() throws Exception {
schedule.addEveryMillisecond(new Runnable() {
@Override
public void run() {
if (isRun) {
reloadConfig();
}
}
}, flushTime);
}
private void reloadConfig() {
isRun = false;
try {
for (String name : dataConfig.getAllConfigName()) {
String filePath = getPath(name);
URL resource = getClass().getClassLoader().getResource(filePath);
if (resource != null) {
boolean result = dataConfig.checkModelAdapter(name, resource.openStream());
if (result) {
dataConfig.reload(name, resource);
}
LOGGER.error(String.format("load file:[%s] is [%s]", name, result ? "success" : "fail"));
File f = new File(URLDecoder.decode(resource.getPath(), "utf-8"));
if (f.exists()) {
f.delete();
}
}
}
} catch (Exception ex) {
LOGGER.warn("{}", ex);
} finally {
isRun = true;
}
}
private String getPath(String name) {
return this.path + name + extension;
}
public boolean flushFile(String fileName, String data) {
byte[] bytes = data.getBytes();
BufferedOutputStream bos = null;
FileOutputStream fos = null;
File file = null;
String filePath = "";
try {
URL resource = getClass().getClassLoader().getResource(path);
if (resource == null) {
resource = checkFolderExist();
}
filePath = resource.getPath();
file = new File(filePath + fileName + bakExtension);
fos = new FileOutputStream(file);
bos = new BufferedOutputStream(fos);
bos.write(bytes);
} catch (Exception e) {
LOGGER.error("write config error.", e);
return false;
} finally {
try {
if (bos != null) {
bos.close();
}
if (fos != null) {
fos.close();
}
} catch (IOException e1) {
LOGGER.error("write config error.", e1);
return false;
}
boolean isSuccess = file.renameTo(new File(filePath + fileName + extension));
if (!isSuccess) {
LOGGER.warn("rename[" + fileName + "]fail");
return false;
}
}
return true;
}
private URL checkFolderExist() {
URL url = ClassLoader.getSystemResource("");
File dir = new File(url.getPath() + path);
if (!dir.exists() && !dir.isDirectory()) {// 判断文件目录是否存在
boolean isSuccess = dir.mkdirs();
if (isSuccess) {
LOGGER.info("create newconfig folder success...");
} else {
LOGGER.warn("create newconfig folder fail");
}
}
return url;
}
}
上一篇: 经验分享:浅谈做好SEO的四个重点问题(新手需注意)
下一篇: Servlet文件上传和下载