前言
相信做Java的童鞋或多或少都聽過反射戈咳,這也應(yīng)該是Java從入門到進(jìn)階的必經(jīng)之路己沛。
但是在我們的實(shí)際開發(fā)中直接使用它們的幾率貌似還是比較少的竹祷,(除了造輪子或者是Spring Mybatis這些框架外
)疆拘。
所以這里介紹一個(gè)在實(shí)際開發(fā)中還是小有用處的反射實(shí)例寝并。
傳統(tǒng)日志
有關(guān)反射的一些基本知識(shí)就不說了箫措,可以自行Google
,也可以看下反射入門衬潦。
日志相信大家都不陌生斤蔓,在實(shí)際開發(fā)中一些比較敏感的數(shù)據(jù)表我們需要對(duì)它的每一次操作都記錄下來。
先來看看傳統(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è)主角來解決了。
利用反射構(gòu)建日志
我們先來先來看下使用反射之后對(duì)代碼所帶來的改變:
@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);
來看下是如何實(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ù)庫中即可闺属。
總結(jié)
反射其實(shí)是非常耗資源的慌盯,再使用過程中還是要慎用。
其中對(duì)method掂器、field亚皂、constructor等對(duì)象做緩存也是很有必要的。
項(xiàng)目地址:https://github.com/crossoverJie/SSM.git
個(gè)人博客地址:http://crossoverjie.top国瓮。
GitHub地址:https://github.com/crossoverJie灭必。