SSM(九) 反射的實際應用 - 構建日誌物件

crossoverJie發表於2017-01-18

前言

相信做Java的童鞋或多或少都聽過反射,這也應該是Java從入門到進階的必經之路。

但是在我們的實際開發中直接使用它們的機率貌似還是比較少的,(除了造輪子或者是Spring Mybatis這些框架外)。

所以這裡介紹一個在實際開發中還是小有用處的反射例項。

傳統日誌

有關反射的一些基本知識就不說了,可以自行Google,也可以看下反射入門

日誌相信大家都不陌生,在實際開發中一些比較敏感的資料表我們需要對它的每一次操作都記錄下來。

先來看看傳統的寫法:

    @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);
    }複製程式碼

非常簡單,就是在儲存完資料表之後再把相同的資料儲存到日誌表中。

但是這樣有以下幾個問題:

  • 如果資料表的欄位較多的話,比如幾百個。那麼日誌表的setter()方法就得寫幾百次,還得是都寫對的情況下。
  • 如果哪天資料表的欄位發生了增加,那麼每個寫日誌的地方都得增加該欄位,提高了維護的成本。

針對以上的情況就得需要反射這個主角來解決了。

利用反射構建日誌

我們先來先來看下使用反射之後對程式碼所帶來的改變:

    @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);
    }複製程式碼

同樣的儲存日誌,不管多少欄位,只需要三行程式碼即可解決。
而且就算之後欄位發生改變寫日誌這段程式碼仍然不需要改動。

其實這裡最主要的一個方法就是CommonUtil.setLogValueModelToModel(content, log);

來看下是如何實現的;

/**
     * 生成日誌實體工具
     *
     * @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);

                    //設定值
                    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);
                }
            }
        }
    }複製程式碼

再使用之前我們首先需要構建好主的資料表,然後new一個日誌表的物件。

setLogValueModelToModel()方法中:

  • 分別獲得資料表和日誌表物件的類型別。
  • 獲取到日誌物件的所有方法集合。
  • 遍歷該集合,並拿到該方法的名稱。
  • 只取其中set開頭的方法,也就是set方法。因為我們需要在迴圈中為日誌物件的每一個欄位賦值。
  • 之後擷取方法名稱獲得具體的欄位名稱。
  • 用之前擷取的欄位名稱,通過getMethod()方法返回資料表中的該欄位的getter方法。
  • 相當於執行了String content = content.getContent();
  • 執行該方法獲得該欄位具體的值。
  • 利用當前迴圈的setter方法為日誌物件的每一個欄位賦值。
  • 相當於執行了log.setContent("asdsf");

其中欄位名稱為LogId時跳出了當前迴圈,因為LogId是日誌表的主鍵,是不需要賦值的。

當迴圈結束時,日誌物件也就構建完成了。之後只需要儲存到資料庫中即可。

總結

反射其實是非常耗資源的,再使用過程中還是要慎用。
其中對method、field、constructor等物件做快取也是很有必要的。

專案地址:github.com/crossoverJi…

個人部落格地址:crossoverjie.top

GitHub地址:github.com/crossoverJi…

相關文章