亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何實現Bean拷貝框架下劃線駝峰互轉擴展支持

發布時間:2021-10-12 13:34:58 來源:億速云 閱讀:170 作者:iii 欄目:編程語言

這篇文章主要講解了“如何實現Bean拷貝框架下劃線駝峰互轉擴展支持”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“如何實現Bean拷貝框架下劃線駝峰互轉擴展支持”吧!

I. 駝峰下劃線拷貝支持

上面的使用都是最基本的使用姿勢,屬性名 + 類型一致,都有getter/setter方法,我們實際的業務場景中,有一個比較重要的地方,就是下劃線與駝峰的轉換支持,如果要使用上面的框架,可以怎樣適配?

1. cglib 下劃線轉駝峰

spring cglib封裝 與 純凈版的cglib 實現邏輯差別不大,主要是spring里面做了一些緩存,所以表現會相對好一點;為了更加通用,這里以純凈版的cglib進行擴展演示

cglib實現轉換的核心邏輯在 net.sf.cglib.beans.BeanCopier.Generator.generateClass

public void generateClass(ClassVisitor v) {
    // ... 省略無關代碼
    PropertyDescriptor[] getters = ReflectUtils.getBeanGetters(source);
    PropertyDescriptor[] setters = ReflectUtils.getBeanSetters(target);
    
    // 掃描source的所有getter方法,寫入到map, key為屬性名; 
    // 為了支持駝峰,下劃線,我們可以擴展一下這個map,如果屬性名為下劃線的,額外加一個駝峰的kv進去 
    Map names = new HashMap();
    for (int i = 0; i < getters.length; i++) {
        names.put(getters[i].getName(), getters[i]);
    }
   
    // ...

    for (int i = 0; i < setters.length; i++) {
        PropertyDescriptor setter = setters[i];
        // 這里根據target的屬性名,獲取source對應的getter方法,同樣適配一下,如果下劃線格式的獲取不到,則改用駝峰的試一下
        PropertyDescriptor getter = (PropertyDescriptor)names.get(setter.getName());
        if (getter != null) {
           // ....
        }
    }
   // ...
}

改造邏輯,上面的注釋中已經貼出來了,核心實現就比較簡單了

提供一個下劃線轉駝峰的工具了 StrUtil

public class StrUtil {
    private static final char UNDER_LINE = '_';

    /**
     * 下劃線轉駝峰
     *
     * @param name
     * @return
     */
    public static String toCamelCase(String name) {
        if (null == name || name.length() == 0) {
            return null;
        }

        if (!contains(name, UNDER_LINE)) {
            return name;
        }

        int length = name.length();
        StringBuilder sb = new StringBuilder(length);
        boolean underLineNextChar = false;

        for (int i = 0; i < length; ++i) {
            char c = name.charAt(i);
            if (c == UNDER_LINE) {
                underLineNextChar = true;
            } else if (underLineNextChar) {
                sb.append(Character.toUpperCase(c));
                underLineNextChar = false;
            } else {
                sb.append(c);
            }
        }

        return sb.toString();
    }

    public static boolean contains(String str, char searchChar) {
        return str.indexOf(searchChar) >= 0;
    }
}

然后自定義一個 PureCglibBeanCopier, 將之前BeanCopier的代碼都拷貝進來,然后改一下上面注釋的兩個地方 (完整的代碼參考項目源碼)

public void generateClass(ClassVisitor v) {
    // ... 省略無關代碼
    PropertyDescriptor[] setters = ReflectUtils.getBeanSetters(target);
    
    // 掃描source的所有getter方法,寫入到map, key為屬性名; 
    // 為了支持駝峰,下劃線,我們可以擴展一下這個map,如果屬性名為下劃線的,額外加一個駝峰的kv進去 
    Map<String, PropertyDescriptor> names = buildGetterNameMapper(source)
   
    // ...

    for (int i = 0; i < setters.length; i++) {
        PropertyDescriptor setter = setters[i];
        // 這里根據target的屬性名,獲取source對應的getter方法,同樣適配一下,如果下劃線格式的獲取不到,則改用駝峰的試一下
        PropertyDescriptor getter = loadSourceGetter(names, setter);
        if (getter != null) {
           // ....
        }
    }
   // ...
}


/**
 * 獲取目標的getter方法,支持下劃線與駝峰
 *
 * @param source
 * @return
 */
public Map<String, PropertyDescriptor> buildGetterNameMapper(Class source) {
    PropertyDescriptor[] getters = org.springframework.cglib.core.ReflectUtils.getBeanGetters(source);
    Map<String, PropertyDescriptor> names = new HashMap<>(getters.length);
    for (int i = 0; i < getters.length; ++i) {
        String name = getters[i].getName();
        String camelName = StrUtil.toCamelCase(name);
        names.put(name, getters[i]);
        if (!name.equalsIgnoreCase(camelName)) {
            // 支持下劃線轉駝峰
            names.put(camelName, getters[i]);
        }
    }
    return names;
}

/**
 * 根據target的setter方法,找到source的getter方法,支持下劃線與駝峰的轉換
 *
 * @param names
 * @param setter
 * @return
 */
public PropertyDescriptor loadSourceGetter(Map<String, PropertyDescriptor> names, PropertyDescriptor setter) {
    String setterName = setter.getName();
    return names.getOrDefault(setterName, names.get(StrUtil.toCamelCase(setterName)));
}

使用姿勢和之前沒有什么區別,就是BeanCopier的創建這里稍稍修改一下即可(BeanCopier可以加緩存,避免頻繁的創建)

public <K, T> T copyAndParse(K source, Class<T> target) throws IllegalAccessException, InstantiationException {
    // todo copier 可以緩存起來,避免每次重新創建
    BeanCopier copier = PureCglibBeanCopier.create(source.getClass(), target, false);
    T res = target.newInstance();
    copier.copy(source, res, null);
    return res;
}

2. hutool 下劃線轉駝峰

hutool也支持下劃線與駝峰的互轉,而且不需要修改源碼, 只用我們自己維護一個FieldMapper即可,改動成本較小;而且在map2bean, bean2map時,可以無修改的實現駝峰下劃線互轉,這一點還是非常很優秀的

/**
 * 駝峰轉換
 *
 * @param source
 * @param target
 * @param <K>
 * @param <T>
 * @return
 */
public <K, T> T copyAndParse(K source, Class<T> target) throws Exception {
    T res = target.newInstance();
    // 下劃線轉駝峰
    BeanUtil.copyProperties(source, res, getCopyOptions(source.getClass()));
    return res;
}

// 緩存CopyOptions(注意這個是HuTool的類,不是Cglib的)

private Map<Class, CopyOptions> cacheMap = new HashMap<>();


private CopyOptions getCopyOptions(Class source) {
    CopyOptions options = cacheMap.get(source);
    if (options == null) {
        // 不加鎖,我們認為重復執行不會比并發加鎖帶來的開銷大
        options = CopyOptions.create().setFieldMapping(buildFieldMapper(source));
        cacheMap.put(source, options);
    }
    return options;
}

/**
 * @param source
 * @return
 */
private Map<String, String> buildFieldMapper(Class source) {
    PropertyDescriptor[] properties = ReflectUtils.getBeanProperties(source);
    Map<String, String> map = new HashMap<>();
    for (PropertyDescriptor target : properties) {
        String name = target.getName();
        String camel = StrUtil.toCamelCase(name);
        if (!name.equalsIgnoreCase(camel)) {
            map.put(name, camel);
        }
        String under = StrUtil.toUnderlineCase(name);
        if (!name.equalsIgnoreCase(under)) {
            map.put(name, under);
        }
    }
    return map;
}

3. mapstruct

最后再介紹一下MapStruct,雖然我們需要手動編碼來實現轉換,但是好處是性能高啊,既然已經手動編碼了,那也就不介意補上下劃線和駝峰的轉換了

@Mappings({
        @Mapping(target = "userName", source = "user_name"),
        @Mapping(target = "market_price", source = "marketPrice")
})
Target2 copyAndParse(Source source);

4. 測試

接下來測試一下上面三個是否能正常工作

定義一個Target2,注意它與Source有兩個字段不同,分別是 user_name/userName, marketPrice/market_price

@Data
public class Target2 {
    private Integer id;
    private String userName;
    private Double price;
    private List<Long> ids;
    private BigDecimal market_price;
}

private void camelParse() throws Exception {
    Source s = genSource();
    Target2 cglib = springCglibCopier.copyAndParse(s, Target2.class);
    Target2 cglib2 = pureCglibCopier.copyAndParse(s, Target2.class);
    Target2 hutool = hutoolCopier.copyAndParse(s, Target2.class);
    Target2 map = mapsCopier.copy(s, Target2.class);
    System.out.println("source:" + s + "\nsCglib:" + cglib + "\npCglib:" + cglib2 + "\nhuTool:" + hutool + "\nMapStruct:" + map);
}

輸出結果如下

source:Source(id=527180337, user_name=一灰灰Blog, price=7.9, ids=[-2509965589596742300, 5995028777901062972, -1914496225005416077], marketPrice=0.35188996791839599609375)
sCglib:Target2(id=527180337, userName=一灰灰Blog, price=7.9, ids=[-2509965589596742300, 5995028777901062972, -1914496225005416077], market_price=0.35188996791839599609375)
pCglib:Target2(id=527180337, userName=一灰灰Blog, price=7.9, ids=[-2509965589596742300, 5995028777901062972, -1914496225005416077], market_price=0.35188996791839599609375)
huTool:Target2(id=527180337, userName=一灰灰Blog, price=7.9, ids=[-2509965589596742300, 5995028777901062972, -1914496225005416077], market_price=0.35188996791839599609375)
MapStruct:Target2(id=527180337, userName=一灰灰Blog, price=7.9, ids=[-2509965589596742300, 5995028777901062972, -1914496225005416077], market_price=0.35188996791839599609375)

性能測試

private <T> void autoCheck2(Class<T> target, int size) throws Exception {
    StopWatch stopWatch = new StopWatch();
    runCopier(stopWatch, "apacheCopier", size, (s) -> apacheCopier.copy(s, target));
    runCopier(stopWatch, "springCglibCopier", size, (s) -> springCglibCopier.copyAndParse(s, target));
    runCopier(stopWatch, "pureCglibCopier", size, (s) -> pureCglibCopier.copyAndParse(s, target));
    runCopier(stopWatch, "hutoolCopier", size, (s) -> hutoolCopier.copyAndParse(s, target));
    runCopier(stopWatch, "springBeanCopier", size, (s) -> springBeanCopier.copy(s, target));
    runCopier(stopWatch, "mapStruct", size, (s) -> mapsCopier.copyAndParse(s,  target));
    System.out.println((size / 10000) + "w -------- cost: " + stopWatch.prettyPrint());
}

對比結果如下,雖然cglib, hutool 支持了駝峰,下劃線的互轉,最終的表現和上面的也沒什么太大區別

1w -------- cost: StopWatch '': running time = 754589100 ns
---------------------------------------------
ns         %     Task name
---------------------------------------------
572878100  076%  apacheCopier yihui

017037900  002%  springCglibCopier
031207500  004%  pureCglibCopier
105254600  014%  hutoolCopier
022156300  003%  springBeanCopier
006054700  001%  mapStruct

1w -------- cost: StopWatch '': running time = 601845500 ns
---------------------------------------------
ns         %     Task name
---------------------------------------------
494895600  082%  apacheCopier
009014500  001%  springCglibCopier
008998600  001%  pureCglibCopier
067145800  011%  hutoolCopier
016557700  003%  springBeanCopier
005233300  001%  mapStruct

10w -------- cost: StopWatch '': running time = 5543094200 ns
---------------------------------------------
ns         %     Task name
---------------------------------------------
4474871900  081%  apacheCopier
089066500  002%  springCglibCopier
090526400  002%  pureCglibCopier
667986400  012%  hutoolCopier
166274800  003%  springBeanCopier
054368200  001%  mapStruct

50w -------- cost: StopWatch '': running time = 27527708400 ns
---------------------------------------------
ns         %     Task name
---------------------------------------------
22145604900  080%  apacheCopier
452946700  002%  springCglibCopier
448455700  002%  pureCglibCopier
3365908800  012%  hutoolCopier
843306700  003%  springBeanCopier
271485600  001%  mapStruct

感謝各位的閱讀,以上就是“如何實現Bean拷貝框架下劃線駝峰互轉擴展支持”的內容了,經過本文的學習后,相信大家對如何實現Bean拷貝框架下劃線駝峰互轉擴展支持這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

松原市| 文登市| 大丰市| 湘阴县| 临湘市| 丹东市| 琼结县| 垦利县| 金平| 海南省| 商都县| 策勒县| 大方县| 深水埗区| 合作市| 建湖县| 广南县| 九江县| 东乌| 沂南县| 剑川县| 鄂托克前旗| 囊谦县| 托里县| 屏山县| 横山县| 正阳县| 五寨县| 甘谷县| 即墨市| 辽宁省| 剑河县| 嘉祥县| 博客| 德庆县| 南昌市| 道真| 繁昌县| 台南市| 枞阳县| 布拖县|