這四種對象屬性拷貝方式朽褪,你都知道嗎衬横?

這四種對象屬性拷貝方式幌绍,你都知道嗎赘娄?(第二種)

一把鉴、BeanCopier

BeanCopier是用于在兩個bean之間進行屬性拷貝的故黑。BeanCopier支持兩種方式:

1、一種是不使用Converter的方式庭砍,僅對兩個bean間屬性名和類型完全相同的變量進行拷貝;
2场晶、另一種則引入Converter,可以對某些特定屬性值進行特殊操作怠缸。

1.1 常規(guī)使用

@Test
public void normalCopy() {
    // 模擬查詢出數據
    UserDO userDO = DataUtil.createData();
    log.info("拷貝前:userDO:{}", userDO);
    // 第一個參數:源對象诗轻, 第二個參數:目標對象,第三個參數:是否使用自定義轉換器(下面會介紹)揭北,下同
    BeanCopier b = BeanCopier.create(UserDO.class, UserDTO.class, false);
    UserDTO userDTO = new UserDTO();
    b.copy(userDO, userDTO, null);
    log.info("拷貝后:userDTO:{}", userDTO);
}
  • 拷貝結果
...... 拷貝前:userDO:UserDO(id=1, userName=Van, sex=0, gmtBroth=2019-11-02T18:24:24.077, balance=100)
...... 拷貝后:userDTO:UserDTO(id=1, userName=Van, sex=null)

通過結果發(fā)現(xiàn):UserDO的int類型的sex無法拷貝到UserDTO的Integer的sex扳炬。.

即:BeanCopier 只拷貝名稱和類型都相同的屬性。

即使源類型是原始類型(int, short和char等)搔体,目標類型是其包裝類型(Integer, Short和Character等)恨樟,或反之:都不會被拷貝。

1.2 自定義轉換器

通過1.1可知疚俱,當源和目標類的屬性類型不同時劝术,不能拷貝該屬性,此時我們可以通過實現(xiàn)Converter接口來自定義轉換器

  • 目標對象屬性類
@Data
public class UserDomain {
    private Integer id;
    private String userName;
    
    /**
     * 以下兩個字段用戶模擬自定義轉換
     */
    private String gmtBroth;
    private String balance;
}

  • 實現(xiàn)Converter接口來自定義屬性轉換
public  class UserDomainConverter implements Converter {

    /**
     * 時間轉換的格式
     */
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    /**
     * 自定義屬性轉換
     * @param value 源對象屬性類
     * @param target 目標對象里屬性對應set方法名,eg.setId
     * @param context 目標對象屬性類
     * @return
     */
    @Override
    public Object convert(Object value, Class target, Object context) {
        if (value instanceof Integer) {
            return value;
        } else if (value instanceof LocalDateTime) {
            LocalDateTime date = (LocalDateTime) value;
            return dtf.format(date);
        } else if (value instanceof BigDecimal) {
            BigDecimal bd = (BigDecimal) value;
            return bd.toPlainString();
        }
        // 更多類型轉換請自定義
        return value;
    }
}

  • 測試方法
/**
 * 類型不同,使用Converter
 */
@Test
public void converterTest() {
    // 模擬查詢出數據
    UserDO userDO = DataUtil.createData();
    log.info("拷貝前:userDO:{}", userDO);
    BeanCopier copier = BeanCopier.create(UserDO.class, UserDomain.class, true);
    UserDomainConverter converter = new UserDomainConverter();
    UserDomain userDomain = new UserDomain();
    copier.copy(userDO, userDomain, converter);
    log.info("拷貝后:userDomain:{}", userDomain);
}

  • 拷貝結果
...... 拷貝前:userDO:UserDO(id=1, userName=Van, gmtBroth=2019-11-02T19:51:11.985, balance=100)
...... 拷貝后:userDomain:UserDomain(id=1, userName=Van, gmtBroth=2019-11-02 19:51:11, balance=100)

  • 注意:
    1呆奕、一旦使用Converter养晋,BeanCopier只使用Converter定義的規(guī)則去拷貝屬性,所以在convert()方法中要考慮所有的屬性梁钾;
    2绳泉、毫無疑問,使用Converter會使對象拷貝速度變慢姆泻。

1.3 緩存BeanCopier實例提升性能

BeanCopier拷貝速度快圈纺,性能瓶頸出現(xiàn)在創(chuàng)建BeanCopier實例的過程中秦忿。 所以,把創(chuàng)建過的BeanCopier實例放到緩存中蛾娶,下次可以直接獲取灯谣,提升性能。

  • 測試代碼
@Test
public void beanCopierWithCache() {

    List<UserDO> userDOList = DataUtil.createDataList(10000);
    long start = System.currentTimeMillis();
    List<UserDTO> userDTOS = new ArrayList<>();
    userDOList.forEach(userDO -> {
        UserDTO userDTO = new UserDTO();
        copy(userDO, userDTO);
        userDTOS.add(userDTO);
    });
}

/**
 * 緩存 BeanCopier
 */
private static final ConcurrentHashMap<String, BeanCopier> BEAN_COPIERS = new ConcurrentHashMap<>();

public void copy(Object srcObj, Object destObj) {
    String key = genKey(srcObj.getClass(), destObj.getClass());
    BeanCopier copier = null;
    if (!BEAN_COPIERS.containsKey(key)) {
        copier = BeanCopier.create(srcObj.getClass(), destObj.getClass(), false);
        BEAN_COPIERS.put(key, copier);
    } else {
        copier = BEAN_COPIERS.get(key);
    }
    copier.copy(srcObj, destObj, null);

}
private String genKey(Class<?> srcClazz, Class<?> destClazz) {
    return srcClazz.getName() + destClazz.getName();
}

1.3 BeanCopier總結

  1. 當源類和目標類的屬性名稱蛔琅、類型都相同胎许,拷貝沒問題。
  2. 當源對象和目標對象的屬性名稱相同罗售、類型不同,那么名稱相同而類型不同的屬性不會被拷貝辜窑。注意,原始類型(int寨躁,short穆碎,char)和 他們的包裝類型,在這里都被當成了不同類型职恳,因此不會被拷貝所禀。
  3. 源類或目標類的setter比getter少,拷貝沒問題放钦,此時setter多余色徘,但是不會報錯。
  4. 源類和目標類有相同的屬性(兩者的getter都存在)操禀,但是目標類的setter不存在褂策,此時會拋出NullPointerException。
  5. 加緩存可以提升拷貝速度颓屑。
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末斤寂,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子揪惦,更是在濱河造成了極大的恐慌遍搞,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丹擎,死亡現(xiàn)場離奇詭異尾抑,居然都是意外死亡,警方通過查閱死者的電腦和手機蒂培,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門再愈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人护戳,你說我怎么就攤上這事翎冲。” “怎么了媳荒?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵抗悍,是天一觀的道長驹饺。 經常有香客問我,道長缴渊,這世上最難降的妖魔是什么赏壹? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮衔沼,結果婚禮上蝌借,老公的妹妹穿的比我還像新娘。我一直安慰自己指蚁,他們只是感情好菩佑,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著凝化,像睡著了一般稍坯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上搓劫,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天瞧哟,我揣著相機與錄音,去河邊找鬼糟把。 笑死绢涡,一個胖子當著我的面吹牛牲剃,可吹牛的內容都是我干的遣疯。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼凿傅,長吁一口氣:“原來是場噩夢啊……” “哼缠犀!你這毒婦竟也來了?” 一聲冷哼從身側響起聪舒,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤辨液,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后箱残,有當地人在樹林里發(fā)現(xiàn)了一具尸體滔迈,經...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年被辑,在試婚紗的時候發(fā)現(xiàn)自己被綠了燎悍。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡盼理,死狀恐怖谈山,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情宏怔,我是刑警寧澤奏路,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布畴椰,位于F島的核電站,受9級特大地震影響鸽粉,放射性物質發(fā)生泄漏斜脂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一触机、第九天 我趴在偏房一處隱蔽的房頂上張望秽褒。 院中可真熱鬧,春花似錦威兜、人聲如沸销斟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蚂踊。三九已至,卻和暖如春笔宿,著一層夾襖步出監(jiān)牢的瞬間犁钟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工泼橘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留涝动,地道東北人。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓炬灭,卻偏偏與公主長得像醋粟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子重归,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內容