
前言
相信做Java的童鞋或多或少都聽過反射,這也應(yīng)該是Java從入門到進(jìn)階的必經(jīng)之路。
但是在我們的實(shí)際開發(fā)中直接使用它們的幾率貌似還是比較少的,(除了造輪子或者是Spring Mybatis這些框架外)。
所以這里介紹一個(gè)在實(shí)際開發(fā)中還是小有用處的反射實(shí)例。
傳統(tǒng)日志
有關(guān)反射的一些基本知識(shí)就不說(shuō)了,可以自行Google,也可以看下反射入門。
日志相信大家都不陌生,在實(shí)際開發(fā)中一些比較敏感的數(shù)據(jù)表我們需要對(duì)它的每一次操作都記錄下來(lái)。
先來(lái)看看傳統(tǒng)的寫法:
@Test
public void insertSelective() throws Exception {
Content content = new Content() ;
content.setContent("asdsf");
content.setCreatedate("2016-12-09");
contentService.insertSelective(content) ;
ContentLog log = new ContentLog();
log.setContentid(content.getContentid());
log.setContent("asdsf");
log.setCreatedate("2016-12-09");
contentLogService.insertSelective(log);
}
非常簡(jiǎn)單,就是在保存完數(shù)據(jù)表之后再把相同的數(shù)據(jù)保存到日志表中。
但是這樣有以下幾個(gè)問題:
- 如果數(shù)據(jù)表的字段較多的話,比如幾百個(gè)。那么日志表的
setter()方法就得寫幾百次,還得是都寫對(duì)的情況下。 - 如果哪天數(shù)據(jù)表的字段發(fā)生了增加,那么每個(gè)寫日志的地方都得增加該字段,提高了維護(hù)的成本。
針對(duì)以上的情況就得需要反射這個(gè)主角來(lái)解決了。
利用反射構(gòu)建日志
我們先來(lái)先來(lái)看下使用反射之后對(duì)代碼所帶來(lái)的改變:
@Test
public void insertSelective2() throws Exception {
Content content = new Content();
content.setContent("你好");
content.setContentname("1");
content.setCreatedate("2016-09-23");
contentService.insertSelective(content);
ContentLog log = new ContentLog();
CommonUtil.setLogValueModelToModel(content, log);
contentLogService.insertSelective(log);
}
同樣的保存日志,不管多少字段,只需要三行代碼即可解決。
而且就算之后字段發(fā)生改變寫日志這段代碼仍然不需要改動(dòng)。
其實(shí)這里最主要的一個(gè)方法就是CommonUtil.setLogValueModelToModel(content, log);
來(lái)看下是如何實(shí)現(xiàn)的;
/**
* 生成日志實(shí)體工具
*
* @param objectFrom
* @param objectTo
*/
public static void setLogValueModelToModel(Object objectFrom, Object objectTo) {
Class<? extends Object> clazzFrom = objectFrom.getClass();
Class<? extends Object> clazzTo = objectTo.getClass();
for (Method toSetMethod : clazzTo.getMethods()) {
String mName = toSetMethod.getName();
if (mName.startsWith("set")) {
//字段名
String field = mName.substring(3);
//獲取from 值
Object value;
try {
if ("LogId".equals(field)) {
continue;
}
Method fromGetMethod = clazzFrom.getMethod("get" + field);
value = fromGetMethod.invoke(objectFrom);
//設(shè)置值
toSetMethod.invoke(objectTo, value);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
再使用之前我們首先需要構(gòu)建好主的數(shù)據(jù)表,然后new一個(gè)日志表的對(duì)象。
在setLogValueModelToModel()方法中:
- 分別獲得數(shù)據(jù)表和日志表對(duì)象的類類型。
- 獲取到日志對(duì)象的所有方法集合。
- 遍歷該集合,并拿到該方法的名稱。
- 只取其中set開頭的方法,也就是set方法。因?yàn)槲覀冃枰谘h(huán)中為日志對(duì)象的每一個(gè)字段賦值。
- 之后截取方法名稱獲得具體的字段名稱。
- 用之前截取的字段名稱,通過
getMethod()方法返回?cái)?shù)據(jù)表中的該字段的getter方法。 - 相當(dāng)于執(zhí)行了
String content = content.getContent(); - 執(zhí)行該方法獲得該字段具體的值。
- 利用當(dāng)前循環(huán)的
setter方法為日志對(duì)象的每一個(gè)字段賦值。 - 相當(dāng)于執(zhí)行了
log.setContent("asdsf");
其中字段名稱為LogId時(shí)跳出了當(dāng)前循環(huán),因?yàn)長(zhǎng)ogId是日志表的主鍵,是不需要賦值的。
當(dāng)循環(huán)結(jié)束時(shí),日志對(duì)象也就構(gòu)建完成了。之后只需要保存到數(shù)據(jù)庫(kù)中即可。
總結(jié)
反射其實(shí)是非常耗資源的,再使用過程中還是要慎用。
其中對(duì)method、field、constructor等對(duì)象做緩存也是很有必要的。
項(xiàng)目地址:https://github.com/crossoverJie/SSM.git
個(gè)人博客地址:http://crossoverjie.top。
GitHub地址:https://github.com/crossoverJie。