利用Java反射读取XML配置文件

利用Java反射读取XML配置文件

背景:项目配置文件分散,有FTP、文件格式、数据库等。且单个任务配置都不一样。故有了统一配置文件的想法,由统一的工具类生成配置对象。

使用:dom4j,Java ioc

首先是XML文件的定义,懒得写schema,直接手写了。配置项由于很多,且分类明确,所以一开始就打算将其分为多个Java类配置。

 1 <? xml version="1.0" encoding="UTF-8" ?>
 2 < Config  id ="STAT1" >
 3      < Stat >
 4          < Property  name ="configName"  value ="custconfig"   />
 5          < Property  name ="descrption"  value ="企业信息处理"   />
 6          < Property  name ="taskType"  value ="1"   />
 7          < Property  name ="expiredMonth"  value ="6"   />
 8          < Property  name ="batchNum"  value ="500"   />
 9      </ Stat >
10      < Ftp >
11          < Property  name ="host"  value ="127.0.0.1"   />
12          < Property  name ="user"  value ="stateg" />
13          < Property  name ="password"  value ="stateg" />
14          < Property  name ="port"  value ="14147" />
15          < Property  name ="remoteDir"  value ="/cust" />
16          < Property  name ="localDir"  value ="D:/ldata_eg/cu-udr" />
17          < Property  name ="fileNamePattern"  value ="CUST_\d{6}.TXT" />
18          < Property  name ="interval"  value ="10" />
19          < Property  name ="reserve"  value ="true" />
20          < Property  name ="maxFileCount"  value ="100" />
21          < Property  name ="checkInterval"  value ="7200" />
22      </ Ftp >
23      < Table >
24          < Record >
25              < Property  name ="tablename"  value ="T_CU_TABLE_NAME"   />
26              < Property  name ="separator"  value =" "   />
27              < Property  name ="startrow"  value ="1"   />
28              < Property  name ="endCol"  value ="5"   />
29          </ Record >
30          < Column  order ="0"  prop ="ID"  type ="VARCHAR2"  length ="32"  key ="yes"   />
31          < Column  order ="1"  prop ="CUSTNAME"  type ="VARCHAR2"  length ="128"  emtpy ="yes"   />
32          < Column  order ="2"  prop ="CUSTAREA"  type ="VARCHAR2"  length ="128"  emtpy ="yes"   />
33          < Column  order ="3"  prop ="CREATEDATE"  type ="DATE" />
34          < Column  order ="4"  prop ="PARENTID"  type ="VARCHAR2"  length ="32"  emtpy ="yes"   />
35          < Column  order ="4"  prop ="UPDATETIME"  type ="DATE" />
36      </ Table >
37 </ Config >
38


接下来,就是XML文件的读取了。使用dom4j是无疑的,可参见http://www.blogjava.net/colorfire/articles/338764.html。

 1 /** */ /**
 2     * 获取配置对象
 3     * @return
 4     * @throws DocumentException
 5     */

 6      public  StatConfig getConfigInfo()  throws  DocumentException  {
 7        SAXReader saxReader = new SAXReader();
 8        Document document = saxReader.read("META-INFO/xml/StatConfigInfo.xml");
 9
10        // 配置入库参数
11        StatConfig statConfig = (StatConfig) reflectConfig(warpPropertyMap(document
12                .selectNodes("//Config/Stat/Property")), StatConfig.class);
13
14        // 配置FTP客户端参数
15        FtpConfig ftpConfig = (FtpConfig) reflectConfig(warpPropertyMap(document.selectNodes("//Config/Ftp/Property")),
16                FtpConfig.class);
17
18        // 配置数据库
19        TableConfig tableConfig = (TableConfig) reflectConfig(warpPropertyMap(document
20                .selectNodes("//Config/Table/Record/Property")), TableConfig.class);
21        List list = document.selectNodes("//Config/Table/Column");
22        Iterator iter = list.iterator();
23        Map<String, ColumnType> colMap = new HashMap<String, ColumnType>();
24        while (iter.hasNext()) {
25            Element element = (Element) iter.next();
26            Map<String, String> configMap = warpAttributeMap(element.attributeIterator());
27            ColumnType colum = (ColumnType) reflectConfig(configMap, ColumnType.class);
28            colMap.put(Integer.toString(colum.getOrder()), colum);
29        }

30        tableConfig.setColMap(colMap);
31
32        statConfig.setFtpConfig(ftpConfig);
33        statConfig.setTableConfig(tableConfig);
34        return statConfig;
35    }

由于XML分块,将数据封装单独抽取成方法。

1 private  Map < String, String >  warpPropertyMap(List list)  {
2        Map<String, String> configMap = new HashMap<String, String>();
3        Iterator iter = list.iterator();
4        while (iter.hasNext()) {
5            Element element = (Element) iter.next();
6            configMap.put(element.attributeValue("name").toLowerCase(), element.attributeValue("value").toLowerCase());
7        }

8        return configMap;
9    }

最后就是将配置数据封装进对象,这里用反射再合适不过了。

 1 public  Object reflectConfig(Map < String, String >  configMap, Class cls)  {
 2        Object config = null;
 3        try {
 4            config = cls.newInstance();
 5            Field[] fields = cls.getDeclaredFields();
 6            for (int i = 0; i < fields.length; i++{
 7                String fieldName = fields[i].getName();
 8                Class fieldType = fields[i].getType();
 9                Method method = cls.getMethod("set" + genMethodName(fieldName), fieldType);
10                String valueString;
11                if ((valueString = configMap.get(fieldName.toLowerCase())) != null{
12                    if (fieldType.equals(String.class)) {
13                        method.invoke(config, valueString);
14                    }
 else if (fieldType.equals(Integer.class)) {
15                        method.invoke(config, Integer.parseInt(valueString));
16                    }
 else if (fieldType.equals(Long.class)) {
17                        method.invoke(config, Long.parseLong(valueString));
18                    }
 else if (fieldType.equals(Boolean.class)) {
19                        method.invoke(config, Boolean.parseBoolean(valueString));
20                    }
 else if (fieldType.equals(Character.class)) {
21                        method.invoke(config, valueString.toCharArray()[0]);
22                    }
 else {
23                        throw new NoSuchFieldException("FtpConfig没有定义的数据类型");
24                    }

25                }

26            }

27        }
 catch (Exception e) {
28            e.printStackTrace();
29        }

30        return config;
31    }

完成,想测试一下,单独写个测试方法吧。
 1 /** */ /**
 2     * 测试方法
 3     * @param obj
 4     * @return
 5     */

 6      public   static  StringBuffer testPOJO(Object obj)  {
 7        Class cls = obj.getClass();
 8        Field[] fields = cls.getDeclaredFields();
 9        StringBuffer resultBuf = new StringBuffer();
10        try {
11            for (int i = 0; i < fields.length; i++{
12                String fieldName = fields[i].getName();
13                Class fieldType = fields[i].getType();
14                Method method;
15                if (fieldType.equals(Boolean.class)) {
16                    method = cls.getMethod("is" + genMethodName(fieldName));
17                }
 else {
18                    method = cls.getMethod("get" + genMethodName(fieldName));
19                }

20                Object res;
21                if ((res = method.invoke(obj)) != null{
22                    String result = res.toString();
23                    resultBuf.append("[" + fieldName + "] = " + result + "\n");
24                }
 else {
25                    resultBuf.append("[" + fieldName + "] = NULL \n");
26                }

27            }

28        }
 catch (Exception e) {
29            e.printStackTrace();
30        }

31        return resultBuf;
32    }


总结:本来考虑的是统一配置后,丢弃ibatis,直接动态生成sql语句执行。但字段名与类属性值匹配又是个麻烦,这里用反射的话就太影响性能了。没想出好方法,再考虑考虑。
后续把测试类改成递归的。

你可能感兴趣的:(利用Java反射读取XML配置文件)