如果你用过mybatis-plus的话,那么你对下面的代码一定不会陌生:
public List<Board> getListByName() {
LambdaQueryWrapper<Board> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Board::getName, "test");
return list(queryWrapper);
}
这时候我们肯能会有这种疑问:框架是如何通过方法引用来获取列名称的呢?来看看mybatis-plus列对应的泛型的定义吧
/**
* 支持序列化的 Function
*
* @author miemie
* @since 2018-05-12
*/
@FunctionalInterface
public interface SFunction<T, R> extends Function<T, R>, Serializable {
}
SerializedLambda:
这里面函数式接口继承了Serializable接口,意思也就是说这个函数式接口是支持序列化的,那么序列化的函数式接口有啥特殊之处呢?SerializedLambda登场了,它是jdk1.8提供的一个新的类,凡是继承了Serializable的函数式接口的实例都可以获取一个属于它的SerializedLambda实例,并且通过它获取到方法的名称,根据我们标准的java bean的定义规则就可以通过方法名称来获取属性名称,属性名称也就是我们数据库中对应的列了。
我们通过一个示例来看看如果通过方法引用来获取对应的方法名称。
首先我们定义2个函数式接口:注意函数式接口一定要继承Serializable接口才能获取方法信息。
get函数是接口:
package com.bsx.test.lambda;
import java.io.Serializable;
@FunctionalInterface
public interface IGetter<T> extends Serializable {
Object get(T source);
}
set函数式接口:
package com.bsx.test.lambda;
import java.io.Serializable;
@FunctionalInterface
public interface ISetter<T, U> extends Serializable {
void set(T t, U u);
}
定义一个实体:Person
package com.bsx.test.entity;
import lombok.Data;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Data
public class Person {
private int id;
private String name;
public Person() {
}
public Person(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
我们写一个根据函数式接口来获取他们对应的方法引用的工具类:
package com.bsx.test.lambda;
import lombok.extern.slf4j.Slf4j;
import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
public class BeanUtils {
private static Map<Class, SerializedLambda> CLASS_LAMDBA_CACHE = new ConcurrentHashMap<>();
/***
* 转换方法引用为属性名
* @param fn
* @return
*/
public static <T> String convertToFieldName(IGetter<T> 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 StringUtils.toLowerCaseFirstOne(methodName.replace(prefix, ""));
}
public static <T, U> String convertToFieldName(ISetter<T, U> fn) {
SerializedLambda lambda = getSerializedLambda(fn);
String methodName = lambda.getImplMethodName();
if(!methodName.startsWith("set")) {
log.warn("无效的setter方法:" + methodName);
}
return StringUtils.toLowerCaseFirstOne(methodName.replace("set", ""));
}
/**
* 关键在于这个方法
*/
public static SerializedLambda getSerializedLambda(Serializable fn) {
SerializedLambda lambda = CLASS_LAMDBA_CACHE.get(fn.getClass());
if(lambda == null) {
try {
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) {
e.printStackTrace();
}
}
return lambda;
}
}
测试一下上面的这个工具类:
@Test
public void testLambdaSerializable() {
String getName = BeanUtils.convertToFieldName(Person::getId);
System.out.println(getName);
String setName = BeanUtils.convertToFieldName(Person::setId);
System.out.println(setName);
}
打印结果如下:
id
name