配置文件的几种读取方式(Java和Lua)

配置文件的几种读取方式(Java和Lua)

Scroll Down

前言

在工作中为了方便项目管理,通常会用到配置文件,以前用的都是配置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文件,再读取数据,每次需要新建一个对应配置类,功能实现比较复杂,节省篇幅删减部分字段

配置文件数据

json格式
fileName对应配置文件名称,新建配置类对应文件字段

@DataFile(fileName = "seize_mine_config")
public class SeizeMineConfig 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读取数据

// 验证消耗
SeizeMineConfig mineConfig = dataConfig.getConfig(IdentiyKey.build(mineId), SeizeMineConfig.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;
	}
}