近来总是在用lambda的方式开发,偶然间发现mybatis-plus的lambda的使用很是新颖....也一直困惑,终于有空最近扒了一下代码 。。。
疑惑通过lambad方式提取出类里面的东东(方法,属性,类名)-好处吗写框架的自然懂
好吧开整。。。。。
模型准备
@Setter
@Getter
@Accessors(chain = true) //这个链式注解很重用主要用于获取带参数的方法
public class Person {
private String name;
private int age;
private float height;
private boolean haveACar;
private BigDecimal money;
}
期望的结果
String methodName = LambdaUtil.getMethodName(Person::getName);
结果是: getName
String methodName = LambdaUtil.getMethodName((Person person) -> person.setAge());
结果是: setAge
#想想一下是不是可以接下来这么玩。。。。。。
String names = LambdaUtil.getMethodNames(Person::getName,Person::getAge,Person::getHight,Person::isHaveACar,Person::getMoney);
结果是: name,age,height,haveACar,money
好吧这里直接上菜啦
https://gitee.com/hihuzi-top/lambda-analysis.git
@Test
void lffSimple() {// 这里直接获取到这个对象的lambda的方法名称(不一定是get或者set开头的代码)
SerializedLambda serializedLambda = AnalysisUtil.lffSimple(Person::isHaveACar);
Assertions.assertEquals("isHaveACar", serializedLambda.getImplMethodName());
}
@Test
void lfSimple() {// 这里直接获取到这个对象的lambda的方法名称(get开头的方法名)
java.lang.invoke.SerializedLambda serializedLambda = AnalysisUtil.lfSimple(Person::getAge);
Assertions.assertEquals("getAge", serializedLambda.getImplMethodName());
}
@Test
void testLfSimple() {// 这里直接获取到这个对象的lambda的方法名称(set开头的方法名)
java.lang.invoke.SerializedLambda serializedLambda = AnalysisUtil.lfSimple(Person::setMoney);
Assertions.assertEquals("setMoney", serializedLambda.getImplMethodName());
}
@Test
void lfS() {
SerializedLambda serializedLambda = AnalysisUtil.lfS(Person::getName);
Assertions.assertEquals("getName", serializedLambda.getImplMethodName());
}
核心代码说明
实现上面的代码主要有两个
1 自定义注解需要继承 java.io.Serializable
@FunctionalInterface
public interface LBiFunction<T, U, R> extends BiFunction<T, U, R>, Serializable {
}
2 通过反射方式获取类信息 返回类型是 java.lang.invoke.SerializedLambda
其实下面两个方法其实是方法重载区别在于是否有参数
/**
* 获取类的对象方法-不带形参(反射方式)
*
* @param function
* @param <T>
* @return
*/
public static <T> java.lang.invoke.SerializedLambda lfSimple(LFunction<T, ?> function) {
Method method = function.getClass().getDeclaredMethods()[1];
method.setAccessible(true);
try {
return (java.lang.invoke.SerializedLambda) method.invoke(function);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
/**
* 获取类的对象方法-带形参(反射方式)
*
* @param function
* @param <T>
* @return
*/
public static <T, U> java.lang.invoke.SerializedLambda lfSimple(LBiFunction<T, U, ?> function) {
Method method = function.getClass().getDeclaredMethods()[1];
method.setAccessible(true);
try {
return (java.lang.invoke.SerializedLambda) method.invoke(function);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
到这里其实已经结束啦 可是呢既然是扒代码是吧。。。那mybatis-plus的实现也是这样吗, 那 为啥返回值是java.lang.invoke.SerializedLambda,纳尼这是啥。。。 好吧慢慢来。。。
mysqlbatis-plus 是通过序列化后直接自己定义的一个SerializedLambda.class 类而且版本号一致(必须一致否这反序列化时无法转换类型)。。
/**
* 获取类的对象方法-不带形参(序列化方式)
*
* @param function
* @param <T>
* @return
*/
public static <T> SerializedLambda lffSimple(LFunction<T, ?> function) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(function);
oos.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
try (ObjectInputStream inputStream = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())) {
Class<?> clazz;
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
clazz = super.resolveClass(desc);
return clazz == java.lang.invoke.SerializedLambda.class ? SerializedLambda.class : clazz;
}
}) {
return (SerializedLambda) inputStream.readObject();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
其实代码不是很难只是用了序列化后在转型为自己定义的SerializedLambda.class
其实也是可以想到SerializedLambda 类肯定也是需要继承 java.lang.invoke.SerializedLambda
public class SerializedLambda implements Serializable {
private static final long serialVersionUID = 8025925345765570181L;
private Class<?> capturingClass;
private String functionalInterfaceClass;
private String functionalInterfaceMethodName;
private String functionalInterfaceMethodSignature;
private String implClass;
private String implMethodName; // 这个就是方法名称啦!!!
private String implMethodSignature;
private int implMethodKind;
private String instantiatedMethodType;
private Object[] capturedArgs;// 如果对象可被序列化其实这里面保存的就是对象的具体值
}
好久没写了,有空在完善。。。。