From e729f04917f76e51ffd33ba175e05e595e69ac10 Mon Sep 17 00:00:00 2001 From: redkale Date: Sat, 12 Aug 2023 14:05:27 +0800 Subject: [PATCH] =?UTF-8?q?Creator=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/redkale/convert/ArrayDecoder.java | 2 +- .../java/org/redkale/source/EntityInfo.java | 2 +- src/main/java/org/redkale/util/Copier.java | 160 +++++++++++++++++- src/main/java/org/redkale/util/Creator.java | 52 ++++-- .../java/org/redkale/util/LambdaSupplier.java | 4 + src/main/java/org/redkale/util/Utility.java | 51 +++++- .../org/redkale/test/util/CopierTest.java | 58 ++++++- 7 files changed, 302 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/redkale/convert/ArrayDecoder.java b/src/main/java/org/redkale/convert/ArrayDecoder.java index 70191f9e6..55b9ec142 100644 --- a/src/main/java/org/redkale/convert/ArrayDecoder.java +++ b/src/main/java/org/redkale/convert/ArrayDecoder.java @@ -59,7 +59,7 @@ public class ArrayDecoder implements Decodeable { } factory.register(type, this); this.componentDecoder = factory.loadDecoder(this.componentType); - this.componentArrayFunction = Creator.arrayFunction(this.componentClass); + this.componentArrayFunction = Creator.funcArray(this.componentClass); } finally { inited = true; lock.lock(); diff --git a/src/main/java/org/redkale/source/EntityInfo.java b/src/main/java/org/redkale/source/EntityInfo.java index 4a766c0dd..a69a44a68 100644 --- a/src/main/java/org/redkale/source/EntityInfo.java +++ b/src/main/java/org/redkale/source/EntityInfo.java @@ -365,7 +365,7 @@ public final class EntityInfo { } this.tableStrategy = dts; - this.arrayer = Creator.arrayFunction(type); + this.arrayer = Creator.funcArray(type); Creator creator = Creator.create(type); String[] cps = null; try { diff --git a/src/main/java/org/redkale/util/Copier.java b/src/main/java/org/redkale/util/Copier.java index d2c6b0ef0..8401c9acd 100644 --- a/src/main/java/org/redkale/util/Copier.java +++ b/src/main/java/org/redkale/util/Copier.java @@ -104,7 +104,7 @@ public interface Copier extends BiFunction { */ public static D copy(final S src, final D dest, final int options) { if (src == null || dest == null) { - return null; + return dest; } return load((Class) src.getClass(), (Class) dest.getClass(), options).apply(src, dest); } @@ -174,6 +174,52 @@ public interface Copier extends BiFunction { return func(srcClass, destClass, 0); } + /** + * 创建源类到目标类的复制器并缓存 + * + * @param 目标类泛型 + * @param 源类泛型 + * @param destClass 目标类名 + * @param srcClass 源类名 + * @param options 可配项 + * + * @return 复制器 + */ + public static Function, Set> funcSet(final Class srcClass, final Class destClass) { + return funcSet(srcClass, destClass, 0); + } + + /** + * 创建源类到目标类的复制器并缓存 + * + * @param 目标类泛型 + * @param 源类泛型 + * @param destClass 目标类名 + * @param srcClass 源类名 + * @param options 可配项 + * + * @return 复制器 + */ + public static Function, List> funcList(final Class srcClass, final Class destClass) { + return funcList(srcClass, destClass, 0); + } + + /** + * 创建源类到目标类的复制器并缓存 + * + * @param 目标类泛型 + * @param 源类泛型 + * @param destClass 目标类名 + * @param srcClass 源类名 + * @param collectionClass 集合类名 + * + * @return 复制器 + */ + public static Function, Collection> funcCollection(final Class srcClass, + final Class destClass, final Class collectionClass) { + return funcCollection(srcClass, destClass, 0, collectionClass); + } + /** * 创建源类到目标类的复制器并缓存 * @@ -188,6 +234,98 @@ public interface Copier extends BiFunction { return load(srcClass, destClass, 0); } + /** + * 创建源类到目标类的复制器并缓存 + * + * @param 目标类泛型 + * @param 源类泛型 + * @param destClass 目标类名 + * @param srcClass 源类名 + * @param options 可配项 + * + * @return 复制器 + */ + public static Function, Set> funcSet(final Class srcClass, final Class destClass, final int options) { + return (Function) funcCollection(srcClass, destClass, options, LinkedHashSet.class); + } + + /** + * 创建源类到目标类的复制器并缓存 + * + * @param 目标类泛型 + * @param 源类泛型 + * @param destClass 目标类名 + * @param srcClass 源类名 + * @param options 可配项 + * + * @return 复制器 + */ + public static Function, List> funcList(final Class srcClass, final Class destClass, final int options) { + return (Function) funcCollection(srcClass, destClass, options, ArrayList.class); + } + + /** + * 创建源类到目标类的复制器并缓存 + * + * @param 目标类泛型 + * @param 源类泛型 + * @param destClass 目标类名 + * @param srcClass 源类名 + * @param options 可配项 + * @param collectionClass 集合类名 + * + * @return 复制器 + */ + public static Function, Collection> funcCollection(final Class srcClass, final Class destClass, + final int options, final Class collectionClass) { + if (destClass == srcClass) { + return CopierInner.funcListOneCaches + .computeIfAbsent(collectionClass, t -> new ConcurrentHashMap<>()) + .computeIfAbsent(options, t -> new ConcurrentHashMap<>()) + .computeIfAbsent(srcClass, v -> { + Creator creator = Creator.create(collectionClass); + Function func = func(srcClass, destClass, options); + Function, Collection> funcList = srcs -> { + if (srcs == null) { + return null; + } else if (srcs.isEmpty()) { + return creator.create(); + } else { + C list = creator.create(); + for (S s : srcs) { + list.add(func.apply(s)); + } + return list; + } + }; + return funcList; + }); + } else { + return CopierInner.funcListTwoCaches + .computeIfAbsent(collectionClass, t -> new ConcurrentHashMap<>()) + .computeIfAbsent(options, t -> new ConcurrentHashMap<>()) + .computeIfAbsent(srcClass, t -> new ConcurrentHashMap<>()) + .computeIfAbsent(destClass, v -> { + Creator creator = Creator.create(collectionClass); + Function func = func(srcClass, destClass, options); + Function, Collection> funcList = srcs -> { + if (srcs == null) { + return null; + } else if (srcs.isEmpty()) { + return (C) creator.create(); + } else { + C list = creator.create(); + for (S s : srcs) { + list.add(func.apply(s)); + } + return list; + } + }; + return funcList; + }); + } + } + /** * 创建源类到目标类的复制器并缓存 * @@ -388,7 +526,7 @@ public interface Copier extends BiFunction { if (nameAlias != null) { return (S src, D dest) -> { if (src == null) { - return null; + return dest; } Map d = (Map) dest; ((Map) src).forEach((k, v) -> { @@ -401,7 +539,7 @@ public interface Copier extends BiFunction { } else { return (S src, D dest) -> { if (src == null) { - return null; + return dest; } Map d = (Map) dest; ((Map) src).forEach((k, v) -> { @@ -415,7 +553,7 @@ public interface Copier extends BiFunction { } else if (nameAlias != null) { return (S src, D dest) -> { if (src == null) { - return null; + return dest; } Map d = (Map) dest; ((Map) src).forEach((k, v) -> { @@ -430,7 +568,7 @@ public interface Copier extends BiFunction { @Override public D apply(S src, D dest) { if (src == null) { - return null; + return dest; } if (options == 0) { ((Map) dest).putAll((Map) src); @@ -1013,6 +1151,18 @@ public interface Copier extends BiFunction { static final ConcurrentHashMap>> funcTwoCaches = new ConcurrentHashMap(); + static final ConcurrentHashMap>> funcListOneCaches = new ConcurrentHashMap(); + + static final ConcurrentHashMap>>> funcListTwoCaches = new ConcurrentHashMap(); + + public static void clear() { + copierOneCaches.clear(); + copierTwoCaches.clear(); + funcOneCaches.clear(); + funcTwoCaches.clear(); + funcListOneCaches.clear(); + funcListTwoCaches.clear(); + } } } diff --git a/src/main/java/org/redkale/util/Creator.java b/src/main/java/org/redkale/util/Creator.java index 6a409f358..12a684f0c 100644 --- a/src/main/java/org/redkale/util/Creator.java +++ b/src/main/java/org/redkale/util/Creator.java @@ -6,6 +6,7 @@ package org.redkale.util; import java.io.*; import java.lang.reflect.*; +import java.math.*; import java.net.*; import java.nio.ByteBuffer; import java.util.AbstractMap.SimpleEntry; @@ -100,10 +101,26 @@ public interface Creator { * * @return IntFunction */ - public static IntFunction arrayFunction(final Class type) { + public static IntFunction funcArray(final Class type) { return CreatorInner.arrayCacheMap.computeIfAbsent(type, CreatorInner::createArrayFunction); } + public static Creator load(Class clazz) { + return CreatorInner.creatorCacheMap.computeIfAbsent(clazz, v -> create(clazz)); + } + + public static Creator register(Class clazz, final Supplier supplier) { + Creator creator = (Object... params) -> supplier.get(); + CreatorInner.creatorCacheMap.put(clazz, creator); + return creator; + } + + public static Creator register(final LambdaSupplier supplier) { + Creator creator = (Object... params) -> supplier.get(); + CreatorInner.creatorCacheMap.put(LambdaSupplier.readClass(supplier), creator); + return creator; + } + /** * 创建指定大小的对象数组 * @@ -144,7 +161,7 @@ public interface Creator { if (type == double.class) { return (T[]) (Object) new double[size]; } - return arrayFunction(type).apply(size); + return funcArray(type).apply(size); } /** @@ -173,10 +190,6 @@ public interface Creator { return (Object... params) -> func.apply(params); } - public static Creator load(Class clazz) { - return CreatorInner.creatorCacheMap.computeIfAbsent(clazz, v -> create(clazz)); - } - /** * 根据指定的class采用ASM技术生产Creator。 * @@ -187,19 +200,29 @@ public interface Creator { */ @SuppressWarnings("unchecked") public static Creator create(Class clazz) { - if (List.class.isAssignableFrom(clazz) && (clazz.isAssignableFrom(ArrayList.class) || clazz.getName().startsWith("java.util.Collections") || clazz.getName().startsWith("java.util.ImmutableCollections") || clazz.getName().startsWith("java.util.Arrays"))) { + if (List.class.isAssignableFrom(clazz) && (clazz.isAssignableFrom(ArrayList.class) + || clazz.getName().startsWith("java.util.Collections") + || clazz.getName().startsWith("java.util.ImmutableCollections") + || clazz.getName().startsWith("java.util.Arrays"))) { clazz = (Class) ArrayList.class; - } else if (Map.class.isAssignableFrom(clazz) && (clazz.isAssignableFrom(HashMap.class) || clazz.getName().startsWith("java.util.Collections") || clazz.getName().startsWith("java.util.ImmutableCollections"))) { + } else if (Map.class.isAssignableFrom(clazz) && (clazz.isAssignableFrom(HashMap.class) + || clazz.getName().startsWith("java.util.Collections") + || clazz.getName().startsWith("java.util.ImmutableCollections"))) { clazz = (Class) HashMap.class; - } else if (Set.class.isAssignableFrom(clazz) && (clazz.isAssignableFrom(HashSet.class) || clazz.getName().startsWith("java.util.Collections") || clazz.getName().startsWith("java.util.ImmutableCollections"))) { + } else if (Set.class.isAssignableFrom(clazz) && (clazz.isAssignableFrom(HashSet.class) + || clazz.getName().startsWith("java.util.Collections") + || clazz.getName().startsWith("java.util.ImmutableCollections"))) { clazz = (Class) HashSet.class; } else if (Map.class.isAssignableFrom(clazz) && clazz.isAssignableFrom(ConcurrentHashMap.class)) { clazz = (Class) ConcurrentHashMap.class; - } else if (Deque.class.isAssignableFrom(clazz) && (clazz.isAssignableFrom(ArrayDeque.class) || clazz.getName().startsWith("java.util.Collections") || clazz.getName().startsWith("java.util.ImmutableCollections"))) { + } else if (Deque.class.isAssignableFrom(clazz) && (clazz.isAssignableFrom(ArrayDeque.class) + || clazz.getName().startsWith("java.util.Collections") + || clazz.getName().startsWith("java.util.ImmutableCollections"))) { clazz = (Class) ArrayDeque.class; } else if (Collection.class.isAssignableFrom(clazz) && clazz.isAssignableFrom(ArrayList.class)) { clazz = (Class) ArrayList.class; - } else if (Map.Entry.class.isAssignableFrom(clazz) && (Modifier.isInterface(clazz.getModifiers()) || Modifier.isAbstract(clazz.getModifiers()) || !Modifier.isPublic(clazz.getModifiers()))) { + } else if (Map.Entry.class.isAssignableFrom(clazz) + && (Modifier.isInterface(clazz.getModifiers()) || Modifier.isAbstract(clazz.getModifiers()) || !Modifier.isPublic(clazz.getModifiers()))) { clazz = (Class) AbstractMap.SimpleEntry.class; } else if (Iterable.class == clazz) { clazz = (Class) ArrayList.class; @@ -572,11 +595,14 @@ public interface Creator { creatorCacheMap.put(ArrayList.class, p -> new ArrayList<>()); creatorCacheMap.put(HashMap.class, p -> new HashMap<>()); creatorCacheMap.put(HashSet.class, p -> new HashSet<>()); + creatorCacheMap.put(LinkedHashSet.class, p -> new LinkedHashSet<>()); creatorCacheMap.put(Stream.class, p -> new ArrayList<>().stream()); creatorCacheMap.put(ConcurrentHashMap.class, p -> new ConcurrentHashMap<>()); creatorCacheMap.put(CompletableFuture.class, p -> new CompletableFuture<>()); creatorCacheMap.put(CompletionStage.class, p -> new CompletableFuture<>()); creatorCacheMap.put(Future.class, p -> new CompletableFuture<>()); + creatorCacheMap.put(AnyValue.DefaultAnyValue.class, p -> new AnyValue.DefaultAnyValue()); + creatorCacheMap.put(AnyValue.class, p -> new AnyValue.DefaultAnyValue()); creatorCacheMap.put(Map.Entry.class, new Creator() { @Override @ConstructorParameters({"key", "value"}) @@ -601,8 +627,6 @@ public interface Creator { return new Class[]{Object.class, Object.class}; } }); - creatorCacheMap.put(AnyValue.DefaultAnyValue.class, p -> new AnyValue.DefaultAnyValue()); - creatorCacheMap.put(AnyValue.class, p -> new AnyValue.DefaultAnyValue()); arrayCacheMap.put(int.class, t -> new int[t]); arrayCacheMap.put(byte.class, t -> new byte[t]); @@ -614,6 +638,8 @@ public interface Creator { arrayCacheMap.put(char.class, t -> new char[t]); arrayCacheMap.put(float.class, t -> new float[t]); arrayCacheMap.put(double.class, t -> new double[t]); + arrayCacheMap.put(BigInteger.class, t -> new BigInteger[t]); + arrayCacheMap.put(BigDecimal.class, t -> new BigDecimal[t]); arrayCacheMap.put(ByteBuffer.class, t -> new ByteBuffer[t]); arrayCacheMap.put(SocketAddress.class, t -> new SocketAddress[t]); arrayCacheMap.put(InetSocketAddress.class, t -> new InetSocketAddress[t]); diff --git a/src/main/java/org/redkale/util/LambdaSupplier.java b/src/main/java/org/redkale/util/LambdaSupplier.java index 79aec9906..592ecfec0 100644 --- a/src/main/java/org/redkale/util/LambdaSupplier.java +++ b/src/main/java/org/redkale/util/LambdaSupplier.java @@ -23,4 +23,8 @@ public interface LambdaSupplier extends Supplier, Serializable { public static String readColumn(LambdaSupplier func) { return Utility.readFieldName(func); } + + public static Class readClass(LambdaSupplier func) { + return Utility.readClassName(func); + } } diff --git a/src/main/java/org/redkale/util/Utility.java b/src/main/java/org/redkale/util/Utility.java index 3522ea486..21ae2db44 100644 --- a/src/main/java/org/redkale/util/Utility.java +++ b/src/main/java/org/redkale/util/Utility.java @@ -51,6 +51,8 @@ public final class Utility { private static final ConcurrentHashMap lambdaFieldNameCache = new ConcurrentHashMap(); + private static final ConcurrentHashMap lambdaClassNameCache = new ConcurrentHashMap(); + private static final Class JAVA_RECORD_CLASS; private static final SecureRandom random = new SecureRandom(); @@ -277,6 +279,10 @@ public final class Utility { return readLambdaFieldName(func); } + public static Class readClassName(LambdaSupplier func) { + return readLambdaClassName(func); + } + private static String readLambdaFieldName(Serializable func) { if (!func.getClass().isSynthetic()) { //必须是Lambda表达式的合成类 throw new RedkaleException("Not a synthetic lambda class"); @@ -310,6 +316,43 @@ public final class Utility { } } + private static Class readLambdaClassName(Serializable func) { + if (!func.getClass().isSynthetic()) { //必须是Lambda表达式的合成类 + throw new RedkaleException("Not a synthetic lambda class"); + } + return lambdaClassNameCache.computeIfAbsent(func.getClass(), clazz -> { + try { + MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(func.getClass(), MethodHandles.lookup()); + MethodHandle mh = lookup.findVirtual(func.getClass(), "writeReplace", MethodType.methodType(Object.class)); + String methodName = ((java.lang.invoke.SerializedLambda) mh.invoke(func)).getImplMethodName(); + String className = methodName.contains("lambda$") ? org.redkale.asm.Type.getReturnType(((java.lang.invoke.SerializedLambda) mh.invoke(func)).getInstantiatedMethodType()).getClassName() + : ((java.lang.invoke.SerializedLambda) mh.invoke(func)).getImplClass().replace('/', '.'); + return (Class) Thread.currentThread().getContextClassLoader().loadClass(className); + } catch (ClassNotFoundException ex) { + throw new RedkaleException(ex); + } catch (Throwable e) { + return readLambdaClassNameFromBytes(func); + } + }); + } + + private static Class readLambdaClassNameFromBytes(Serializable func) { + try { + ObjectWriteStream out = new ObjectWriteStream(new ByteArrayOutputStream()); + out.writeObject(func); + out.close(); + String className = out.classNameReference.get(); + if (className != null) { + return (Class) Thread.currentThread().getContextClassLoader().loadClass(className); + } else { + //native-image环境下获取不到methodName + throw new RedkaleException("cannot found method-name from lambda " + func); + } + } catch (Exception e) { + throw new RedkaleException(e); + } + } + static String readFieldName(String methodName) { String name; if (methodName.startsWith("is")) { @@ -332,6 +375,8 @@ public final class Utility { public final ObjectReference methodNameReference = new ObjectReference<>(); + public final ObjectReference classNameReference = new ObjectReference<>(); + public ObjectWriteStream(OutputStream out) throws IOException { super(out); } @@ -339,7 +384,11 @@ public final class Utility { @Override protected Object replaceObject​(Object obj) throws IOException { if (obj instanceof java.lang.invoke.SerializedLambda) { - methodNameReference.set(((java.lang.invoke.SerializedLambda) obj).getImplMethodName()); + String methodName = ((java.lang.invoke.SerializedLambda) obj).getImplMethodName(); + methodNameReference.set(methodName); + String className = methodName.contains("lambda$") ? org.redkale.asm.Type.getReturnType(((java.lang.invoke.SerializedLambda) obj).getInstantiatedMethodType()).getClassName() + : ((java.lang.invoke.SerializedLambda) obj).getImplClass().replace('/', '.'); + classNameReference.set(className); } return super.replaceObject(obj); } diff --git a/src/test/java/org/redkale/test/util/CopierTest.java b/src/test/java/org/redkale/test/util/CopierTest.java index 31a22a87e..e2a20896b 100644 --- a/src/test/java/org/redkale/test/util/CopierTest.java +++ b/src/test/java/org/redkale/test/util/CopierTest.java @@ -5,6 +5,7 @@ package org.redkale.test.util; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; import org.junit.jupiter.api.*; import org.redkale.convert.json.JsonConvert; import org.redkale.util.*; @@ -36,6 +37,7 @@ public class CopierTest { test.run17(); test.run18(); test.run19(); + test.run20(); } @Test @@ -283,12 +285,34 @@ public class CopierTest { @Test public void run19() throws Exception { Bean0 bean1 = new Bean0(); - bean1.setCartype(111); + bean1.setCarid(111); bean1.setUsername("aaa"); Bean0 bean2 = new Bean0(); Copier.load(Bean0.class, Bean0.class, Copier.OPTION_SKIP_NULL_VALUE).apply(bean1, bean2); System.out.println(JsonConvert.root().convertTo(bean2)); Assertions.assertEquals("aaa", bean2.getUsername()); + } + + @Test + public void run20() throws Exception { + Bean0 bean1 = new Bean0(); + bean1.setCarid(111L); + bean1.setUsername("aaa"); + Bean0 bean2 = new Bean0(); + bean2.setCarid(111L); + bean2.setUsername("bbb"); + Creator.register(Bean0::new); + + System.out.println(Copier.load(Bean0.class, Bean0.class).apply(bean1, new Bean0())); + + Function, Set> funcSet = Copier.funcSet(Bean0.class, Bean0.class); + Set set = funcSet.apply(Arrays.asList(bean1, bean2)); + Assertions.assertEquals(1, set.size()); + + Function, List> funcList = Copier.funcList(Bean0.class, Bean0.class); + List list = funcList.apply(Arrays.asList(bean1, bean2)); + Assertions.assertEquals(2, list.size()); + System.out.println("------------------------------------------"); } @@ -347,6 +371,28 @@ public class CopierTest { this.username = username; } + @Override + public int hashCode() { + int hash = 5; + hash = 59 * hash + (int) (this.carid ^ (this.carid >>> 32)); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Bean0 other = (Bean0) obj; + return this.carid == other.carid; + } + @Override public String toString() { return JsonConvert.root().convertTo(this); @@ -354,7 +400,7 @@ public class CopierTest { } - public class Bean1 { + public static class Bean1 { public String intval; @@ -364,7 +410,7 @@ public class CopierTest { } } - public class Bean2 { + public static class Bean2 { public int intval; @@ -374,7 +420,7 @@ public class CopierTest { } } - public class Bean3 { + public static class Bean3 { private Long seqno; @@ -392,7 +438,7 @@ public class CopierTest { } } - public class Bean4 { + public static class Bean4 { private String seqno; @@ -410,7 +456,7 @@ public class CopierTest { } } - public class Bean5 { + public static class Bean5 { private int seqno;