Browse Source

!171 修复 Lambda 构造器在 JDK16 下无法使用的问题
Merge pull request !171 from 你有医保你先上/3.0

青苗 4 years ago
parent
commit
6ccc026659

+ 10 - 2
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/LambdaUtils.java

@@ -52,13 +52,21 @@ public final class LambdaUtils {
     public static <T> LambdaMeta extract(SFunction<T, ?> func) {
         try {
             Method method = func.getClass().getDeclaredMethod("writeReplace");
-            return new SerializedLambdaMeta((SerializedLambda) ReflectionKit.setAccessible(method).invoke(func));
+            return new ReflectLambdaMeta((SerializedLambda) ReflectionKit.setAccessible(method).invoke(func));
         } catch (NoSuchMethodException e) {
-            if (func instanceof Proxy) return new ProxyLambdaMeta((Proxy) func);
+            // IDEA 调试模式下 lambda 表达式是一个代理
+            if (func instanceof Proxy) return new IdeaProxyLambdaMeta((Proxy) func);
             String message = "Cannot find method writeReplace, please make sure that the lambda composite class is currently passed in";
             throw new MybatisPlusException(message);
         } catch (InvocationTargetException | IllegalAccessException e) {
             throw new MybatisPlusException(e);
+        } catch (RuntimeException e) {
+            // JDK 16 模块化后不能访问 java.lang.invoke.SerializedLambda 了,走序列化路线
+            // https://gitee.com/baomidou/mybatis-plus/issues/I3XDT9
+            if (e.getClass().getName().equals("java.lang.reflect.InaccessibleObjectException")) {
+                return new ShadowLambdaMeta(com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda.extract(func));
+            }
+            throw e;
         }
     }
 

+ 4 - 2
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/support/ProxyLambdaMeta.java → mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/support/IdeaProxyLambdaMeta.java

@@ -8,9 +8,11 @@ import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Proxy;
 
 /**
+ * 在 IDEA 的 Evaluate 中执行的 Lambda 表达式元数据需要使用该类处理元数据
+ * <p>
  * Create by hcl at 2021/5/17
  */
-public class ProxyLambdaMeta implements LambdaMeta {
+public class IdeaProxyLambdaMeta implements LambdaMeta {
     private static final Field FIELD_MEMBER_NAME;
     private static final Field FIELD_MEMBER_NAME_CLAZZ;
     private static final Field FIELD_MEMBER_NAME_NAME;
@@ -30,7 +32,7 @@ public class ProxyLambdaMeta implements LambdaMeta {
     private final Class<?> clazz;
     private final String name;
 
-    public ProxyLambdaMeta(Proxy func) {
+    public IdeaProxyLambdaMeta(Proxy func) {
         InvocationHandler handler = Proxy.getInvocationHandler(func);
         try {
             Object dmh = ReflectionKit.setAccessible(handler.getClass().getDeclaredField("val$target")).get(handler);

+ 2 - 2
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/support/SerializedLambdaMeta.java → mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/support/ReflectLambdaMeta.java

@@ -10,7 +10,7 @@ import java.lang.reflect.Field;
 /**
  * Created by hcl at 2021/5/14
  */
-public class SerializedLambdaMeta implements LambdaMeta {
+public class ReflectLambdaMeta implements LambdaMeta {
     private static final Field FIELD_CAPTURING_CLASS;
 
     static {
@@ -24,7 +24,7 @@ public class SerializedLambdaMeta implements LambdaMeta {
 
     private final SerializedLambda lambda;
 
-    public SerializedLambdaMeta(SerializedLambda lambda) {
+    public ReflectLambdaMeta(SerializedLambda lambda) {
         this.lambda = lambda;
     }
 

+ 58 - 0
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/support/SerializedLambda.java

@@ -0,0 +1,58 @@
+package com.baomidou.mybatisplus.core.toolkit.support;
+
+import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
+
+import java.io.*;
+
+/**
+ * 当前类是 {@link java.lang.invoke.SerializedLambda } 的一个镜像
+ * <p>
+ * Create by hcl at 2020/7/17
+ */
+@SuppressWarnings("ALL")
+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;
+
+    public static SerializedLambda extract(Serializable serializable) {
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
+             ObjectOutputStream oos = new ObjectOutputStream(baos)) {
+            oos.writeObject(serializable);
+            oos.flush();
+            try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())) {
+                @Override
+                protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
+                    Class<?> clazz = super.resolveClass(desc);
+                    return clazz == java.lang.invoke.SerializedLambda.class ? SerializedLambda.class : clazz;
+                }
+
+            }) {
+                return (SerializedLambda) ois.readObject();
+            }
+        } catch (IOException | ClassNotFoundException e) {
+            throw new MybatisPlusException(e);
+        }
+    }
+
+    public String getInstantiatedMethodType() {
+        return instantiatedMethodType;
+    }
+
+    public Class<?> getCapturingClass() {
+        return capturingClass;
+    }
+
+    public String getImplMethodName() {
+        return implMethodName;
+    }
+}

+ 29 - 0
mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/toolkit/support/ShadowLambdaMeta.java

@@ -0,0 +1,29 @@
+package com.baomidou.mybatisplus.core.toolkit.support;
+
+import com.baomidou.mybatisplus.core.toolkit.ClassUtils;
+
+/**
+ * 基于 {@link SerializedLambda} 创建的元信息
+ * <p>
+ * Create by hcl at 2021/7/7
+ */
+public class ShadowLambdaMeta implements LambdaMeta {
+    private final SerializedLambda lambda;
+
+    public ShadowLambdaMeta(SerializedLambda lambda) {
+        this.lambda = lambda;
+    }
+
+    @Override
+    public String getImplMethodName() {
+        return lambda.getImplMethodName();
+    }
+
+    @Override
+    public Class<?> getInstantiatedClass() {
+        String instantiatedMethodType = lambda.getInstantiatedMethodType();
+        String instantiatedType = instantiatedMethodType.substring(2, instantiatedMethodType.indexOf(';')).replace('/', '.');
+        return ClassUtils.toClassConfident(instantiatedType, lambda.getCapturingClass().getClassLoader());
+    }
+
+}

+ 2 - 0
mybatis-plus-core/src/test/java/com/baomidou/mybatisplus/test/toolkit/LambdaUtilsTest.java

@@ -18,6 +18,7 @@ package com.baomidou.mybatisplus.test.toolkit;
 import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
 import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta;
 import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
+import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
 import lombok.Getter;
 import org.junit.jupiter.api.Test;
 
@@ -44,6 +45,7 @@ class LambdaUtilsTest {
         test(function);
         MethodHandles.Lookup lookup = MethodHandles.lookup();
         MethodHandle getter = lookup.findVirtual(TestModel.class, "getId", MethodType.methodType(int.class));
+        assertNotNull(SerializedLambda.extract(function));
         function = (SFunction<TestModel, Object>) MethodHandleProxies.asInterfaceInstance(SFunction.class, getter);
         test(function);
     }