源码分析:Mybatis-plus lambda参数拼接

Mybatis-Plus Student::getName
之前写过上面这个,今天对源码 debug一下

1
2
3
4
5
6
7
8
9
10
11
# 1. 为什么 Mybatis Plus 要重新写类 SerializedLambda(后面版本改为 "writeReplace" 调用获取)
没有final??? 没有readResolve()???,就可以实例化为 SerializedLambda

# 2. debug 条件的应用,有时候会失败,不知道为什么

# 3. 调用方法不同
Student::getName
// #28 invokevirtual com/taopanfeng/junit/mylambda/Student.getName:()Ljava/lang/String;

e -> e.getName()
// #33 invokestatic com/taopanfeng/junit/mylambda/wq_methodref/_wq_methodRef.lambda$main$0:(Lcom/taopanfeng/junit/mylambda/Student;)Ljava/lang/String;

debug Mybatis-plus 日志打印

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 开启日志
logging.level.com.amoros.metric.generator.prompt.PromptConfigMapper=debug

# 打印的日志
----
[2024-05-10 09:40:49.051] DEBUG [http-nio-18006-exec-5] [1788745977201803264] ==> Preparing: SELECT id,tenant_id,type,code,name,description,page,module,date_created,last_modified,create_by,create_by_name,update_by,update_by_name FROM prompt_config WHERE (tenant_id IN (?,?) AND type = ?) - com.amoros.metric.generator.prompt.PromptConfigMapper.selectList#debug:137
[2024-05-10 09:40:49.063] DEBUG [http-nio-18006-exec-5] [1788745977201803264] ==> Parameters: hotjet(String), amoros(String), 1(Integer) - com.amoros.metric.generator.prompt.PromptConfigMapper.selectList#debug:137
[2024-05-10 09:40:49.092] DEBUG [http-nio-18006-exec-5] [1788745977201803264] <== Total: 51 - com.amoros.metric.generator.prompt.PromptConfigMapper.selectList#debug:137
----

# 主要找 tenant_id 和 type 根据lambda如何获取到名称 "tenant_id IN (?,?) AND type = ?"
.in(PromptConfig::getTenantId, qry.getTenantId(), amoros)
.eq(PromptConfig::getType, qry.getType())

# debug => 条件 msg.contains("SELECT")
ch.qos.logback.classic.Logger.buildLoggingEventAndAppend

# 这一步拼接SQL
MappedStatement.getBoundSql

# debug => 条件 handler.handleToken(expression.toString()).contains("tenant_id IN")
GenericTokenParser.parse

# debug => 条件 normal.getSqlSegment().contains("tenant_id IN") && !cacheSqlSegment
MergeSegments.getSqlSegment

# debug => 条件 this instanceof NormalSegmentList
AbstractISegmentList.getSqlSegment

# SFunction 解析为 SerializedLambda ===> 获取名称
AbstractWrapper.in
AbstractLambdaWrapper.columnToString(SFunction)
AbstractLambdaWrapper.getColumn
SerializedLambda.resolve

代码演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 2024-05-10 11:09
public static void main(String[] args) throws Throwable {
SFunction<PromptConfig, String> f1 = PromptConfig::getTenantId;// getTenantId
Function<PromptConfig, String> f11 = PromptConfig::getTenantId;// getTenantId
// SerializedLambda resolve1 = SerializedLambda.resolve(f1);// Mybatis Plus
// String methodName1 = resolve1.getImplMethodName();

SFunction<PromptConfig, String> f2 = e -> e.getTenantId();// lambda$main$7796d039$1
// SerializedLambda resolve2 = SerializedLambda.resolve(f2);// Mybatis Plus
// String methodName2 = resolve2.getImplMethodName();
// String columnName = PropertyNamer.methodToProperty(methodName);


Method method = f1.getClass().getDeclaredMethod("writeReplace");
method.setAccessible(true);
SerializedLambda invoke = (SerializedLambda) method.invoke(f1);// JDK

//--------------------------------

// Function<PromptConfig, String> f3 = PromptConfig::getTenantId;
// Function<PromptConfig, String> f4 = (Function<PromptConfig, String> & Serializable)PromptConfig::getTenantId;
//
// boolean b = f3 instanceof Serializable;
// boolean b2 = f4 instanceof Serializable;

// Function<PromptConfig, String> f4 = (Function & Tag) f3;
// Function<PromptConfig, String> f4 = tagging(f3);

// boolean b = f3 instanceof Tag;
// boolean b2 = f4 instanceof Tag;
}

// 标签化
// public static Function<PromptConfig, String> tagging(Function<PromptConfig, String> s) {
// return (Function<PromptConfig, String> & Tag) s::apply;
// }
//
// // 是否是标签
// public static <T> boolean isTag(T t) {
// return t instanceof Tag;
// }

// INTERNAL. Zero abstract method (ZAM) / tagging interface.
// interface Tag {
// }

wq_再写方法引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package com.taopanfeng.junit.mylambda.wq_methodref;

import cn.hutool.core.io.FastByteArrayOutputStream;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.SerializeUtil;
import com.baomidou.mybatisplus.core.toolkit.ClassUtils;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import com.baomidou.mybatisplus.core.toolkit.SerializationUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
import com.taopanfeng.junit.mylambda.Student;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.function.Function;

/**
* 描述
*
* @author 陶攀峰
* @date 2024-05-10 11:54
*/
public class _wq_methodRef {
// 2024-05-10 11:54
public static void main(String[] args) throws Throwable {
// Function<Student, String> f = Student::getName;
// public Object apply(Object var1) {
// return ((Student)var1).getName();
// }
// // javap -p -c -v _wq_methodRef.class > 1.txt
// #28 invokevirtual com/taopanfeng/junit/mylambda/Student.getName:()Ljava/lang/String;

// Function<Student, String> f = e -> e.getName();
// public Object apply(Object var1) {
// return _wq_methodRef.lambda$main$0((Student)var1);
// }
// // javap -p -c -v _wq_methodRef.class > 2.txt
// #33 invokestatic com/taopanfeng/junit/mylambda/wq_methodref/_wq_methodRef.lambda$main$0:(Lcom/taopanfeng/junit/mylambda/Student;)Ljava/lang/String;

// Function<Student, String> f = new Function<Student, String>() {
// @Override
// public String apply(Student student) {
// return student.getName();
// }
// };

// try (ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(SerializationUtils.serialize(lambda))) {
// @Override
// protected Class<?> resolveClass(ObjectStreamClass objectStreamClass) throws IOException, ClassNotFoundException {
// Class<?> clazz;
// try {
// clazz = ClassUtils.toClassConfident(objectStreamClass.getName());
// } catch (Exception ex) {
// clazz = super.resolveClass(objectStreamClass);
// }
// return clazz == java.lang.invoke.SerializedLambda.class ? SerializedLambda.class : clazz;
// }


Function<Student, String> func = (Function<Student, String> & Serializable) Student::getName;
byte[] serialize = SerializeUtil.serialize(func);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(serialize)) {
@Override
protected Class<?> resolveClass(ObjectStreamClass objectStreamClass) throws IOException, ClassNotFoundException {
Class<?> clazz = Class.forName(objectStreamClass.getName());
// return clazz;// 失败 => 不能反射 JDK有 readResolve() 方法,导致不能序列化为 SerializedLambda
return clazz == java.lang.invoke.SerializedLambda.class ? SerializedLambda.class : clazz;
}
};
Object o = ois.readObject();// SerializedLambda
if (o instanceof SerializedLambda) {
SerializedLambda lambda = (SerializedLambda) o;
System.out.println("-------- 1 -> " + lambda.getImplMethodName());
}

Method method = func.getClass().getDeclaredMethod("writeReplace");
method.setAccessible(true);
Object o1 = method.invoke(func);
if (o1 instanceof java.lang.invoke.SerializedLambda) {
java.lang.invoke.SerializedLambda lambda = (java.lang.invoke.SerializedLambda) o1;
System.out.println("-------- 2 -> " + lambda.getImplMethodName());
}
}
}