利用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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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
}
2
3
4
5
6

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

25
26
27
28
29
30
31
32
33
34
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
}

2
3
4

5
6
7
8
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
}

2
3

4
5
6

7
8
9
10
11

12

13
14

15
16

17
18

19
20

21
22

23
24
25
26
27

28
29
30
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
}
2
3
4
5
6

7
8
9
10

11

12
13
14
15

16
17

18
19
20
21

22
23
24

25
26
27
28

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