利用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 extends Serializable {
    Object apply(T source);
}
/**
 * setter方法接口定义
 */
@FunctionalInterface
public interface ISetter extends Serializable {
    void accept(T t, U u);
}

2. 定义getter/setter引用转换属性名的工具类

public class BeanUtils {
    ...
    /**
     * 缓存类-Lambda的映射关系
     */
    private static Map CLASS_LAMDBA_CACHE = new ConcurrentHashMap<>();

    /***
     * 转换方法引用为属性名
     * @param fn
     * @return
     */
    public static  String convertToFieldName(IGetter 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  String convertToFieldName(ISetter 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 - 简单高效的轻代码开发框架

你可能感兴趣的:(利用Lambda实现通过getter/setter方法引用拿到属性名)