利用Lambda實現經過getter/setter方法引用拿到屬性名

不少開發場景須要用到Java Bean的屬性名,直接寫死屬性名字符串的形式容易產生bug(屬性名一旦變化,IDE不會告訴你你的字符串須要同步修改)。JDK8的Lambda能夠經過方法引用簡化代碼,一樣也能夠經過getter/setter的方法引用拿到屬性名,避免潛在的bug。

指望實現效果

// 傳統方式:hard code寫死屬性名
// String ITEM_NAME = "orgName";
// 方法引用:替代hard code字符串,當屬性名變化時IDE會同步提示,避免未同步產生bug
String ITEM_NAME = BeanUtils.convertToFieldName(User::getOrgName);

具體實現代碼封裝

1. 定義FunctionalInterface 接收方法引用

/**
 * getter方法接口定義
 */
@FunctionalInterface
public interface IGetter<T> extends Serializable {
    Object apply(T source);
}
/**
 * setter方法接口定義
 */
@FunctionalInterface
public interface ISetter<T, U> extends Serializable {
    void accept(T t, U u);
}

2. 定義getter/setter引用轉換屬性名的工具類

public class BeanUtils {
    ...
    /**
     * 緩存類-Lambda的映射關係
     */
    private static Map<Class, SerializedLambda> CLASS_LAMDBA_CACHE = new ConcurrentHashMap<>();

    /***
     * 轉換方法引用爲屬性名
     * @param fn
     * @return
     */
    public static <T> String convertToFieldName(IGetter<T> fn) {
        SerializedLambda lambda = getSerializedLambda(fn);
        String methodName = lambda.getImplMethodName();
        String prefix = null;
        if(methodName.startsWith("get")){
            prefix = "get";
        }
        else if(methodName.startsWith("is")){
            prefix = "is";
        }
        if(prefix == null){
            log.warn("無效的getter方法: "+methodName);
        }
        // 截取get/is以後的字符串並轉換首字母爲小寫(S爲diboot項目的字符串工具類,可自行實現)
        return S.uncapFirst(S.substringAfter(methodName, prefix));
    }
    
    /***
     * 轉換setter方法引用爲屬性名
     * @param fn
     * @return
     */
    public static <T,R> String convertToFieldName(ISetter<T,R> fn) {
        SerializedLambda lambda = getSerializedLambda(fn);
        String methodName = lambda.getImplMethodName();
        if(!methodName.startsWith("set")){
            log.warn("無效的setter方法: "+methodName);
        }
        // 截取set以後的字符串並轉換首字母爲小寫(S爲diboot項目的字符串工具類,可自行實現)
        return S.uncapFirst(S.substringAfter(methodName, "set"));
    }
    
    /***
     * 獲取類對應的Lambda
     * @param fn
     * @return
     */
    private static SerializedLambda getSerializedLambda(Serializable fn){
        //先檢查緩存中是否已存在
        SerializedLambda lambda = CLASS_LAMDBA_CACHE.get(fn.getClass());
        if(lambda == null){
            try{//提取SerializedLambda並緩存
                Method method = fn.getClass().getDeclaredMethod("writeReplace");
                method.setAccessible(Boolean.TRUE);
                lambda = (SerializedLambda) method.invoke(fn);
                CLASS_LAMDBA_CACHE.put(fn.getClass(), lambda);
            }
            catch (Exception e){
                log.error("獲取SerializedLambda異常, class="+fn.getClass().getSimpleName(), e);
            }
        }
        return lambda;
    }
}

3. 開心的引用

String ITEM_NAME = BeanUtils.convertToFieldName(User::getOrgName);

Diboot - 簡單高效的輕代碼開發框架java

相關文章
相關標籤/搜索