mybatisplus中凭什么可以使用Lambda来构建语句?
本文主要讲解两个内容
- mybatisplus中Lambda构建语句的时候是怎么通过方法引用得到相关表字段的。
- java序列化中的一些内置方法(扩展知道的可以略过)
- 通过方法引用得到其方法名称
- 源码中是如何实现的的
问题引入
熟悉mybatisplus的小伙伴应该清楚 可以使用Lambda来构建查询、更新语句,举个栗子
@Data
@TableName("user") //表名
class User{
private String name;
private String password;
private Integer age;
private String email;
}
new LambdaQueryWrapper<User>().eq(User::getAge, 1);
上述代码的意义是 构建查询user表中 age=1的用户,这里有个问题是我们传入的是一个方法引用为什么可以得到 'age’这个字段。
如果我们传的是一个class对象那我们可以通过下面的代码来得到具体的字段名。
Method getAge = user.getClass().getDeclaredMethod("getAge");
getAge.getName().substring(3).toLowerCase()
那如果是一个方法引用该如何获取到方法的名称呢?
可以通过 SerializedLambda 去获取函数的名称 可以看到在成员变量中有一个functionalInterfaceMethodName,他就保存了方法引用的函数名,通过注释可以了解到可以通过 writeReplace方法得到。那么writeReplace又是什么呢?
/**
Lambda 表达式的序列化形式。此类的属性表示 Lambda 工厂站点中存在的信息,包括静态元工厂参数(例如主功能接口方法的标识和实现方法的标识)以及动态元工厂参数(例如在捕获 Lambda 时从词法范围捕获的值)。
SerializedLambda 的实现者(例如编译器或语言运行时库)应确保实例正确反序列化。实现此目的的一种方法是确保writeReplace方法返回SerializedLambda的实例,而不是允许默认序列化继续进行。
SerializedLambda有一个readResolve方法,该方法在捕获类中查找名为$deserializeLambda$(SerializedLambda)的(可能是私有的)静态方法,将其自身作为第一个参数来调用,并返回结果。实现$deserializeLambda$的 Lambda 类负责验证SerializedLambda的属性是否与该类实际捕获的 lambda 一致。
通过反序列化序列化形式生成的函数对象的身份是不可预测的,因此身份敏感的操作(例如引用相等,对象锁定和System.identityHashCode()可能会在不同的实现中产生不同的结果,甚至在同一实现中的不同反序列化时也会产生不同的结果。
*/
public final class SerializedLambda implements Serializable {
@java.io.Serial
private static final long serialVersionUID = 8025925345765570181L;
/**
* The capturing class.
*/
private final Class<?> capturingClass;
/**
* The functional interface class.
*/
private final String functionalInterfaceClass;
/**
* The functional interface method name.
*/
private final String functionalInterfaceMethodName;
/**
* The functional interface method signature.
*/
private final String functionalInterfaceMethodSignature;
/**
* The implementation class.
*/
private final String implClass;
/**
* The implementation method name.
*/
private final String implMethodName;
/**
* The implementation method signature.
*/
private final String implMethodSignature;
/**
* The implementation method kind.
*/
private final int implMethodKind;
/**
* The instantiated method type.
*/
private final String instantiatedMethodType;
/**
* The captured arguments.
*/
@SuppressWarnings("serial") // Not statically typed as Serializable
private final Object[] capturedArgs;
上面的注释是我直接翻译源码中的注释,可能会不通顺。但是我们可以翻译后的注释和类变量得到一些有用的信息
- SerializedLambda 是用来保存Lambda表达式的序列化形式会去保存具体的实现方法名称(implMethodName字段)
- 要实现Serializable接口,因为必须实现Serializable接口,jvm才会调用writeReplace。
- writeReplace的实现是由编译器或语言运行时库来完成的,也就是说只要是Lambda并且是继承了Serializable接口就可以调用对象的writeReplace方法返回一个SerializedLambda 对象。
java序列化
大家知道java对象要进行网络传输或者本地储存就必须要实现Serializable接口,Serializable接口没有方法或字段,仅用于标识可序列化的语义。但是在jvm内部会调用几个特殊的方法来完成java对象的序列化,打开Serializable的源码在注释上会有这样一段话
/**
* Serializability of a class is enabled by the class implementing the
* java.io.Serializable interface. Classes that do not implement this
* interface will not have any of their state serialized or
* deserialized. All subtypes of a serializable class are themselves
* serializable. The serialization interface has no methods or fields
* and serves only to identify the semantics of being serializable. <p>
*
* To allow subtypes of non-serializable classes to be serialized, the
* subtype may assume responsibility for saving and restoring the
* state of the supertype's public, protected, and (if accessible)
* package fields. The subtype may assume this responsibility only if
* the class it extends has an accessible no-arg constructor to
* initialize the class's state. It is an error to declare a class
* Serializable if this is not the case. The error will be detected at
* runtime. <p>
*
* During deserialization, the fields of non-serializable classes will
* be initialized using the public or protected no-arg constructor of
* the class. A no-arg constructor must be accessible to the subclass
* that is serializable. The fields of serializable subclasses will
* be restored from the stream. <p>
*
* When traversing a graph, an object may be encountered that does not
* support the Serializable interface. In this case the
* NotSerializableException will be thrown and will identify the class
* of the non-serializable object. <p>
*
* Classes that require special handling during the serialization and
* deserialization process must implement special methods with these exact
* signatures:
*
* <PRE>
* private void writeObject(java.io.ObjectOutputStream out)
* throws IOException
* private void readObject(java.io.ObjectInputStream in)
* throws IOException, ClassNotFoundException;
* private void readObjectNoData()
* throws ObjectStreamException;
* </PRE>
*
* <p>The writeObject method is responsible for writing the state of the
* object for its particular class so that the corresponding
* readObject method can restore it. The default mechanism for saving
* the Object's fields can be invoked by calling
* out.defaultWriteObject. The method does not need to concern
* itself with the state belonging to its superclasses or subclasses.
* State is saved by writing the individual fields to the
* ObjectOutputStream using the writeObject method or by using the
* methods for primitive data types supported by Dat