一、核心思路
核心思路是: 获取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 的静态方法(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();
}
}
}
网友评论