05 Lambda表达式获取字段名
2024年9月2日大约 2 分钟
Lambda表达式获取字段名
背景
参考 Mybatis-Plus 实现通过Lambda的方式获取字段名、方法名、类名
// mybati plus 查询实例
this.lambdaQuery()
.eq(User::getName, dto.getName())
.eq(User::getPhone, dto.getPhone())
.list();
原理
如果生成的lambda类需要实现java.io.Serializable的话,那么在生成的lambda实现类中,就会有一个名为writeReplace的方法来作该lambda类的序列化支持(直观的,可见下面的示例)。writeReplace的返回值是SerializedLambda,我们通过反射调用拿到writeReplace返回的SerializedLambda对象后,就可以获得你所写的lambda表达式中所涉及到的类、方法等信息了(看一下SerializedLambda的构造,就知道可以拿到哪些东西了):
SFunction 接口
import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.util.function.Function;
/**
* 集成了{@link Serializable}能力的{@link Function}
* <pre>
* 当然,获得{@link SerializedLambda},不一定非要使用Function,使用其他的{@link FunctionalInterface}也行,
* 只要保证以下两点即可:
* 1. 会生成lambda实现类
* 2. 生成的lambda实现类要实现Serializable
* 注:如果只是获取SerializedLambda实例本身,那么除了上述FunctionalInterface的方式外,还可以通过其它方式获得(如:序列化/反序列化等)。
* </pre>
*
* @author h.t.l
* @since 2024/8/30 10:39
*/
public interface SFunction<T, R> extends Function<T, R>, Serializable {
}
Person 类
import lombok.Data;
import java.io.Serializable;
@Data
// @SuppressWarnings("all")
public class Person{
private String name;
private String nickName;
}
SerializedLambdaUtil 工具类
::: code-tabs#java
@tab v1 自定义封装
// SerializedLambdaUtil.class
import org.apache.commons.lang3.StringUtils;
import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* {@link SerializedLambda}工具类
*
* @author h.t.l
* @since 2024/8/29 17:18
*/
public class SerializedLambdaUtil_V1 {
public static FieldNameParser defaultFieldNameParser = new FieldNameParser(){};
/**
* @see SerializedLambdaUtil_V1#getImplClassLongName(SFunction)
*/
public static <T> String getImplClassLongName(SFunction<T, ?> sFunction) {
return getImplClassLongName(getSerializedLambda(sFunction));
}
/**
* @see SerializedLambdaUtil_V1#getFieldName(SFunction)
*/
public static <T> String getFieldName(SFunction<T, ?> sFunction) {
return getFieldName(sFunction, defaultFieldNameParser);
}
/**
* 获取字段名称
*/
public static <T> String getFieldName(SFunction<T, ?> sFunction, FieldNameParser fieldNameParser) {
return getFieldName(getSerializedLambda(sFunction), fieldNameParser);
}
/**
* 获取lambda表达式字段名称
* <pre>
* 假设你的lambda表达式部分是这样写的:<code>Person::getFirstName</code>,
* 那么,此方法的目的就是获取到getFirstName方法对应的(Person类中的对应字段的)字段名
* </pre>
*/
public static String getFieldName(SerializedLambda serializedLambda, FieldNameParser fieldNameParser) {
String implClassLongName = getImplClassLongName(serializedLambda);
String implMethodName = getImplMethodName(serializedLambda);
try {
return fieldNameParser.parseFieldName(Class.forName(implClassLongName), implMethodName);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
/**
* 获取lambda表达式中,实现方法的方法名
* <p>
* 说明:
* 假设你的lambda表达式部分是这样写的:<code>Person::getFirstName</code>,<br/>
* 那么这里获取到的就是Person.getFirstName()的方法名getFirstName
* </p>
*
* @param serializedLambda
* serializedLambda对象
* @return 实现方法的方法名 <br />
* 形如:getFirstName
*/
private static String getImplMethodName(SerializedLambda serializedLambda) {
return serializedLambda.getImplMethodName();
}
/**
* 获取lambda表达式中,实现方法的类的全类名
* <p>
* 说明:
* 假设你的lambda表达式部分是这样写的:<code>Person::getFirstName</code>,<br/>
* 那么这里获取到的就是Person的全类名,形如:<code>com.example.lambda.test.Person</code>
* </p>
*
* @param serializedLambda
* serializedLambda对象
* @return 实现方法的类的全类名 <br />
* 形如:com.example.lambda.test.Person
*/
private static String getImplClassLongName(SerializedLambda serializedLambda) {
return serializedLambda.getImplClass().replace("/", ".");
}
/**
* 获取SerializedLambda实例
*
* @param potentialLambda
* lambda实例
* @return SerializedLambda实例
*/
private static <T extends Serializable> SerializedLambda getSerializedLambda(T potentialLambda) {
try{
Class<?> potentialLambdaClass = potentialLambda.getClass();
// lambda类属于合成类
if (!potentialLambdaClass.isSynthetic()) {
throw new IllegalArgumentException("potentialLambda must be lambda-class");
}
Method writeReplaceMethod = potentialLambdaClass.getDeclaredMethod("writeReplace");
boolean isAccessible = writeReplaceMethod.isAccessible();
writeReplaceMethod.setAccessible(true);
Object writeReplaceObject = writeReplaceMethod.invoke(potentialLambda);
writeReplaceMethod.setAccessible(isAccessible);
if (writeReplaceObject == null || !SerializedLambda.class.isAssignableFrom(writeReplaceObject.getClass())) {
throw new IllegalArgumentException("potentialLambda must be lambda-class. writeReplaceObject should not be " + writeReplaceObject);
}
return (SerializedLambda)writeReplaceObject;
} catch( NoSuchMethodException | IllegalAccessException | InvocationTargetException e ){
throw new IllegalArgumentException("potentialLambda must be lambda-class", e);
}
}
/**
* 字段名解析器
*/
public interface FieldNameParser {
/**
* 解析字段名
* <pre>
* 假设你的lambda表达式部分是这样写的:<code>Person::getFirstName</code>,
* 那么,
* clazz就对应Person类
* methodName就对应getFirstName
* </pre>
*
* @param clazz
* 字段所在的类
* @param methodName
* 与字段相关的方法(如:该字段的getter方法)
* @return 解析字段名
*/
default String parseFieldName(Class<?> clazz, String methodName) {
return StringUtils.uncapitalize(methodName.substring("get".length()));
}
}
}
@tab:active v2 MyBatis-Plus 封装
// SerializedLambdaUtil.class
import cn.hutool.core.annotation.AnnotationUtil;
import com.baomidou.mybatisplus.core.toolkit.ClassUtils;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.apache.ibatis.reflection.property.PropertyNamer;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @author h.t.l
* @since 2024/9/2 17:23
*/
public class SerializedLambdaUtil {
private static final Map<Class<?>, Map<String, FieldCache>> COLUMN_CACHE_MAP = new ConcurrentHashMap<>();
public static <T> String getFieldName(SFunction<T, ?> func) {
// 使用 mybatisplus 的lambda工具类
LambdaMeta meta = LambdaUtils.extract(func);
Class<?> instantiatedClass = meta.getInstantiatedClass();
Map<String, FieldCache> fieldMap = getFieldMap(instantiatedClass);
String implMethodName = meta.getImplMethodName();
String fieldName = PropertyNamer.methodToProperty(implMethodName);
return fieldMap.get(fieldName).getFieldName();
}
public static <T> FieldCache getFieldCache(SFunction<T, ?> func) {
// 使用 mybatisplus 的lambda工具类
LambdaMeta meta = LambdaUtils.extract(func);
Class<?> instantiatedClass = meta.getInstantiatedClass();
Map<String, FieldCache> fieldMap = getFieldMap(instantiatedClass);
String implMethodName = meta.getImplMethodName();
String fieldName = PropertyNamer.methodToProperty(implMethodName);
return fieldMap.get(fieldName);
}
public static Map<String, FieldCache> getFieldMap(Class<?> clazz) {
return CollectionUtils.computeIfAbsent(COLUMN_CACHE_MAP, clazz, key -> {
Map<String, FieldCache> map = new HashMap<>();
List<Field> fieldList = getAllFields(key);
for (int i = 0; i < fieldList.size(); i++) {
Field field = fieldList.get(i);
FieldCache fieldCache = new FieldCache(clazz, field, i);
map.put(fieldCache.getFieldName(), fieldCache);
}
return map;
});
}
public static List<Field> getAllFields(Class<?> clazz) {
List<Field> fieldList = ReflectionKit.getFieldList(ClassUtils.getUserClass(clazz));
return new ArrayList<>(fieldList);
}
@Data
@AllArgsConstructor
public static class FieldCache implements Serializable {
private static final long serialVersionUID = -59220189172227917L;
private Class<?> clazz;
private Field field;
private String fieldName;
private Integer sort; // 排序,从0开始
private Map<Class<? extends Annotation>, ? extends Annotation> fieldAnnotations;
public FieldCache(Class<?> clazz, Field field, Integer sort) {
this.clazz = clazz;
this.field = field;
this.sort = sort;
this.fieldName = field.getName();
this.fieldAnnotations = Arrays.stream(AnnotationUtil.getAnnotations(field, false))
.collect(Collectors.toMap(Annotation::annotationType, Function.identity()));
}
}
}
:::
main 测试
public static void main(String[] args) {
/* ------------------------- 测试 SerializedLambdaUtil工具类 ------------------------- */
System.out.println();
System.out.println("通过FunctionalInterface方式,获得字段名:" + SerializedLambdaUtil.getFieldName(Person::getName));
System.out.println("通过FunctionalInterface方式,获得字段名:" + SerializedLambdaUtil.getFieldName(Person::getNickName));
}
// 输出结果
通过FunctionalInterface方式,获得字段名:name
通过FunctionalInterface方式,获得字段名:nickName
dump出 Lambda 运行时生成Class
- JDK 8 (硬编码方式)
static { System.setProperty("jdk.internal.lambda.dumpProxyClasses", "."); }
- JDK 11 ( jvm参数方式)硬编码无效,原因是模块化导致的
jvm参数: -Djdk.internal.lambda.dumpProxyClasses=/target/lambda-classes
Lambda生成类 - 反编译后的源码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.jdk.lombok.functionalInterface;
import java.lang.invoke.SerializedLambda;
import java.lang.invoke.LambdaForm.Hidden;
// $FF: synthetic class
final class MainTests$$Lambda$1 implements SFunction {
private MainTests$$Lambda$1() {
}
@Hidden
public Object apply(Object var1) {
return ((Person)var1).getName();
}
private final Object writeReplace() {
return new SerializedLambda(MainTests.class,
"com/jdk/lombok/functionalInterface/SFunction",
"apply",
"(Ljava/lang/Object;)Ljava/lang/Object;",
5,
"com/jdk/lombok/functionalInterface/Person",
"getName",
"()Ljava/lang/String;",
"(Lcom/jdk/lombok/functionalInterface/Person;)Ljava/lang/Object;",
new Object[0]);
}
}
涉及源码核心类
- java.lang.invoke.LambdaMetafactory
- java.lang.invoke.InnerClassLambdaMetafactory
- java.io.Serializable
- java.lang.invoke.SerializedLambda
- java.util.function.Function
- java.lang.FunctionalInterface