美文网首页
java 根据getter和setter方法的Lambda表达式

java 根据getter和setter方法的Lambda表达式

作者: 饱饱想要灵感 | 来源:发表于2024-09-18 14:38 被阅读0次

    一、核心思路

    核心思路是: 获取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();
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:java 根据getter和setter方法的Lambda表达式

          本文链接:https://www.haomeiwen.com/subject/kqmnljtx.html