java 根据getter和setter方法的Lambda表达式获取字段名

一、核心思路

核心思路是: 获取SerializedLambda类, 通过它获取方法名, 然后裁掉 set 和 get/is 前缀, 最后转换为驼峰命名.

SerializedLambda是Java提供的关于lambda表达式的序列化方案,会将实现了Serializable接口的lambda表达式转换成 SerializedLambda 对象之后再去做序列化。

其核心在于Java在对lambda表达式序列化时,虚拟机会添加一个writeReplace()方法。

根据Java的序列化机制,虚拟机在调用write(obj)序列化对象前,如果被序列化对象有writeReplace方法,则会先调用该方法,用该方法返回的对象进行序列化,即序列化对象被替换了。

同理,lambda表达式在序列化前也会调用writeReplace(),然后返回一个SerializedLambda 对象(真正的被序列化的对象),该对象包含了lambda表达式的所有信息,比如函数名implMethodName、函数签名implMethodSignature等,这些信息都是以字段形式存在的,这样就解决了lambda序列化的问题。

既然被序列化的对象有writeReplace()方法,那么我们也可以直接调用该方法获取相应的SerializedLambda对象。

SerializedLambda 源码类上的注释有四段, 大致如下:
段落一:SerializedLambda是Lambda表达式的序列化形式,这类存储了Lambda表达式的运行时信息。
段落二:编译器需确保生成的 lambda 类的实例会提供一个writeReplace 方法,且该方法会返回一个SerializedLambda实例。
段落三:SerializedLambda提供了readResolve方法,其职能为调用 capturingClass 的静态方法deserializeLambda(SerializedLambda)并且把自身实例作为入参。capturingClass 即为 lambda 表达式定义所在的类。
段落四: 序列化和反序列化产生的函数对象的身份敏感操作的标识形式(如System.identityHashCode()、对象锁定等等)是不可预测的。

二、代码实现

import java.io.Serializable;
import java.util.function.Function;

@FunctionalInterface
public interface IGetter<T, R> extends Function<T, R>, Serializable {
}
import java.io.Serializable;
import java.util.function.BiConsumer;

@FunctionalInterface
public interface ISetter<T, U> extends BiConsumer<T, U>, Serializable {
}
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.ibatis.reflection.property.PropertyNamer;

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;

@Slf4j
public class LambdaBeanUtils {

    public static <T, R> String getMethodName(IGetter<T, R> fn) {
        try {
            return getSerializedLambda(fn).getImplMethodName();
        } catch (Exception e) {
            log.error("通过getter的方法引用获取方法名失败", e);
            return null;
        }
    }

    public static <T, R> String getMethodName(ISetter<T, R> fn) {
        try {
            return getSerializedLambda(fn).getImplMethodName();
        } catch (Exception e) {
            log.error("通过setter的方法引用获取方法名失败", e);
            return null;
        }
    }


    public static <T, R> String getColumn(IGetter<T, R> fn) {
        try {
            return PropertyNamer.methodToProperty(getSerializedLambda(fn).getImplMethodName());
        } catch (Exception e) {
            log.error("通过getter的方法引用获取数据库字段名失败", e);
            return null;
        }
    }

    /**
     * 通过getter的方法引用获取字段名
     */
    public static <T, R> String getFieldName(IGetter<T, R> fn) {
        try {
            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.error("无效的getter方法: " + methodName);
            }

            return toLowerCaseFirstOne(methodName.replace(prefix, ""));
        } catch (Exception e) {
            log.error("通过getter的方法引用获取字段名失败", e);
            return null;
        }
    }

    /**
     * 通过setter的方法引用获取字段名
     * @throws Exception
     */
    public static <T, U> String getFieldName(ISetter<T, U> fn) {
        try {
            SerializedLambda lambda = getSerializedLambda(fn);
            String methodName = lambda.getImplMethodName();
            if (!methodName.startsWith("set")) {
                log.error("无效的setter方法:" + methodName);
            }
            return toLowerCaseFirstOne(methodName.replace("set", ""));
        } catch (Exception e) {
            log.error("通过setter的方法引用获取字段名失败", e);
            return null;
        }

    }

   /**
    * 关键在于这个方法
    */
    private static SerializedLambda getSerializedLambda(Serializable fn) throws Exception {
        Method method = fn.getClass().getDeclaredMethod("writeReplace");
        method.setAccessible(Boolean.TRUE);
        SerializedLambda lambda = (SerializedLambda) method.invoke(fn);
        return lambda;
    }

    /**
     * 字符串首字母转小写
     */
    private static String toLowerCaseFirstOne(String field) {
        if (ObjectUtils.isEmpty(field)) {
            return field;
        }
        if (Character.isLowerCase(field.charAt(0))) {
            return field;
        } else {
            char firstOne = Character.toLowerCase(field.charAt(0));
            String other = field.substring(1);
            return new StringBuilder().append(firstOne).append(other).toString();
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容