一.问题的引入
在做项目时有一个文件(数据表),里面的内容全部是类似于key-value的形式,如果一个字段一个字段的从文件或者数据表中将数据加载到内存中,一旦数据较多,将是一个比较繁琐的工作,那么我们怎样在程序编译时就能确保所有的字段都加载到内存中,并自动转换成自己想要的数据格式?(如八大基本数据类型,Map,List等),关于这个问题,我采用了以下的做法,希望有大牛指出更为巧妙的方法。
二.问题的解决
1.假定数据库中有一张名称为init的数据表,里面包含了大量的key-value数据。
2.我们先编写注解Init.java,代码如下:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Init {
// 别名-如果文件中的key名字过长,便于自己重命名
String otherName() default "";
// 数据类型的转换方法
String transMethod() default "";
}
3.编写我们对应的数据实体类ConfigData.java:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ConfigData {
@Init
public static String name;
@Init
public static int age;
@Init
public static float money;
@Init
public static double rate;
@Init(transMethod = "toList")
public static List<String> awards;
@Init(transMethod = "toMap")
public static Map<String, Integer> items;
@Init(otherName = "faiid")
public static double familysAndPersonalIDNumber;
public ConfigData() {
}
// 类型转换方法
public List<String> toList(String confString) {
List<String> list = new ArrayList<>();
String[] pairStrings = confString.split(";");
for (String str : pairStrings) {
list.add(str);
}
return list;
}
// 类型转换方法
public Map<String, Integer> toMap(String confString) {
Map<String, Integer> map = new HashMap<>();
String[] pairStrings = confString.split(";");
for (String str : pairStrings) {
String[] str2 = str.split(":");
map.put(str2[0], Integer.parseInt(str2[1]));
}
return map;
}
}
4.编写解析工具类ConfigUtil.java:
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.nutz.ioc.impl.PropertiesProxy;
public class ConfigUtil {
/**
* 配置文件的转换
*
* @param prop
* 配置文件
* @param classPath
* 数据实体类名
*/
public static void change(PropertiesProxy prop, String classPath) {
try {
// 获取配置类
Class<?> paramSetClass = Class.forName(classPath);
ConfigData cd = (ConfigData) paramSetClass.newInstance();
// 设置配置字段
Field[] fields = paramSetClass.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
// 字段上是否有注解
boolean hasAnnotation = field.isAnnotationPresent(Init.class);
if (hasAnnotation) {
// 获取字段类型名称
String filedTypeStr = field.getType().getName();
Init keyValueAnnotation = field.getAnnotation(Init.class);
// 获取别名
String filedName = keyValueAnnotation.otherName();
// 获取解析方法
String parseMethodName = keyValueAnnotation.transMethod();
String filedValue = getFieldValue(prop, field, filedName);
try {
setValue(cd, field, filedTypeStr, parseMethodName,
filedValue);
} catch (Exception e) {
e.printStackTrace();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取配置文件中字段的值
*
* @param prop
* 配置文件
* @param field
* 字段属性
* @param filedName
* 字段名称
* @return
* @throws Exception
*/
private static String getFieldValue(PropertiesProxy prop, Field field,
String filedName) throws Exception {
String filedValue = null;
// 如果是别名,获取真实名称
if (filedName.equals("")) {
filedValue = prop.get(field.getName());
} else {
filedValue = prop.get(filedName);
}
if (filedValue == null) {
throw new Exception("config Field '" + field.getName()
+ "' not find");
}
return filedValue;
}
/**
* 设置数据实体的名称,类型,值
*
* @param cd
* 数据实体类
* @param field
* 字段属性
* @param filedTypeStr
* 字段类型名称
* @param parseMethodName
* 解析方法名称
* @param filedValue
* 字段值
* @throws Exception
*/
private static void setValue(ConfigData cd, Field field,
String filedTypeStr, String parseMethodName, String filedValue)
throws Exception {
switch (filedTypeStr) {
case "short":
field.set(cd, Short.parseShort(filedValue));
break;
case "int":
field.set(cd, Integer.parseInt(filedValue));
break;
case "float":
field.set(cd, Float.parseFloat(filedValue));
break;
case "double":
field.set(cd, Double.parseDouble(filedValue));
break;
case "long":
field.set(cd, Long.parseLong(filedValue));
break;
case "boolean":
field.set(cd, Boolean.parseBoolean(filedValue));
break;
case "java.lang.String":
field.set(cd, filedValue);
break;
// 针对对象类型,如:map,list等
default:
if (parseMethodName.equals("")) {
throw new Exception(
"非short,int,float,double,long,String类型,必须指定解析方法");
}
Method method = cd.getClass().getMethod(parseMethodName,
String.class);
Object obj = method.invoke(cd, filedValue);
field.set(cd, obj);
break;
}
}
}
注意在以上代码片中我们使用了Nutz框架的PropertiesProxy,一个比Java的Properties更强大的键值配置类,感兴趣的朋友可以了解一下Nutz框架,号称是SSH的一个替代品。
5.读取配置文件:的方法:
public void load() {
// 定义数据库工具,这里我们采用Nutz的Dao
NutDao dao;
// 获取数据
List<Record> list = dao.query("init", null);
PropertiesProxy prop = new PropertiesProxy();
for (Record record : list) {
prop.put(record.getString("key"), record.getString("value"));
}
//自动加载配置
ConfUtil.change(prop, ConfigData.class.getName());
}
本人Java初学,如果有更好的方法,欢迎交流!