From f338bf41a884da4f03b5ae4ee29e369f69ad0fe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=87=8C=E6=98=9F?= Date: Wed, 13 Sep 2023 23:19:17 +0800 Subject: [PATCH] =?UTF-8?q?bugfix:=E4=BF=AE=E5=A4=8Dstorage=E5=8A=A0?= =?UTF-8?q?=E5=85=A5record=E7=B1=BB=E5=AF=BC=E8=87=B4=E7=94=9F=E6=88=90?= =?UTF-8?q?=E5=8D=8F=E8=AE=AE=E6=8A=A5=E9=94=99bug;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../protocol/registration/EnhanceUtils.java | 48 ++++++++++++++----- .../registration/ProtocolAnalysis.java | 33 +++++++++---- .../registration/ProtocolRegistration.java | 10 ++++ .../com/zfoo/protocol/util/ClassUtils.java | 6 ++- .../zfoo/protocol/util/ReflectionUtils.java | 12 ++++- .../storage/export/ExportBinaryTesting.java | 5 +- 6 files changed, 87 insertions(+), 27 deletions(-) diff --git a/protocol/src/main/java/com/zfoo/protocol/registration/EnhanceUtils.java b/protocol/src/main/java/com/zfoo/protocol/registration/EnhanceUtils.java index 3e979440..d15ebfe5 100644 --- a/protocol/src/main/java/com/zfoo/protocol/registration/EnhanceUtils.java +++ b/protocol/src/main/java/com/zfoo/protocol/registration/EnhanceUtils.java @@ -30,6 +30,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.util.*; +import java.util.stream.Collectors; /** * 对应于ProtocolRegistration @@ -181,6 +182,9 @@ public abstract class EnhanceUtils { var fieldRegistrations = registration.getFieldRegistrations(); var packetClazz = constructor.getDeclaringClass(); + if (packetClazz.isRecord()) { + fields = registration.getOriginalFields(); + } var builder = new StringBuilder(); builder.append("{").append(packetClazz.getCanonicalName() + " packet = (" + packetClazz.getCanonicalName() + ")$2;"); @@ -204,28 +208,46 @@ public abstract class EnhanceUtils { // see: ProtocolRegistration.read() private static String readMethodBody(ProtocolRegistration registration) { var constructor = registration.getConstructor(); - var fields = registration.getFields(); var fieldRegistrations = registration.getFieldRegistrations(); var builder = new StringBuilder(); builder.append("{").append("if(!" + EnhanceUtils.byteBufUtilsReadBoolean + "){").append("return null;}"); var packetClazz = constructor.getDeclaringClass(); - builder.append(packetClazz.getCanonicalName() + " packet=new " + packetClazz.getCanonicalName() + "();"); + if (packetClazz.isRecord()) { + var fields = registration.getOriginalFields(); + List constructorParam = new ArrayList<>(fields.length); + for (var i = 0; i < fields.length; i++) { + var field = fields[i]; + var fieldRegistration = fieldRegistrations[i]; + // protocol backwards compatibility,协议向后兼容 + if (field.isAnnotationPresent(Compatible.class)) { + builder.append("if(!$1.isReadable()){ return packet; }"); + } - for (var i = 0; i < fields.length; i++) { - var field = fields[i]; - var fieldRegistration = fieldRegistrations[i]; - // protocol backwards compatibility,协议向后兼容 - if (field.isAnnotationPresent(Compatible.class)) { - builder.append("if(!$1.isReadable()){ return packet; }"); + var readObject = enhanceSerializer(fieldRegistration.serializer()).readObject(builder, field, fieldRegistration); + constructorParam.add(readObject); } - var readObject = enhanceSerializer(fieldRegistration.serializer()).readObject(builder, field, fieldRegistration); + builder.append(packetClazz.getCanonicalName() + " packet=new " + packetClazz.getCanonicalName() + "(" + constructorParam.stream().collect(Collectors.joining(StringUtils.COMMA)) + ");"); + } else { + var fields = registration.getFields(); + builder.append(packetClazz.getCanonicalName() + " packet=new " + packetClazz.getCanonicalName() + "();"); - if (Modifier.isPublic(field.getModifiers())) { - builder.append(StringUtils.format("packet.{}={};", field.getName(), readObject)); - } else { - builder.append(StringUtils.format("packet.{}({});", ReflectionUtils.fieldToSetMethod(packetClazz, field), readObject)); + for (var i = 0; i < fields.length; i++) { + var field = fields[i]; + var fieldRegistration = fieldRegistrations[i]; + // protocol backwards compatibility,协议向后兼容 + if (field.isAnnotationPresent(Compatible.class)) { + builder.append("if(!$1.isReadable()){ return packet; }"); + } + + var readObject = enhanceSerializer(fieldRegistration.serializer()).readObject(builder, field, fieldRegistration); + + if (Modifier.isPublic(field.getModifiers())) { + builder.append(StringUtils.format("packet.{}={};", field.getName(), readObject)); + } else { + builder.append(StringUtils.format("packet.{}({});", ReflectionUtils.fieldToSetMethod(packetClazz, field), readObject)); + } } } diff --git a/protocol/src/main/java/com/zfoo/protocol/registration/ProtocolAnalysis.java b/protocol/src/main/java/com/zfoo/protocol/registration/ProtocolAnalysis.java index 9ce08746..4af45a61 100644 --- a/protocol/src/main/java/com/zfoo/protocol/registration/ProtocolAnalysis.java +++ b/protocol/src/main/java/com/zfoo/protocol/registration/ProtocolAnalysis.java @@ -42,6 +42,7 @@ import javassist.NotFoundException; import java.io.IOException; import java.lang.reflect.*; import java.util.*; +import java.util.Map.Entry; import static com.zfoo.protocol.ProtocolManager.*; @@ -311,6 +312,7 @@ public class ProtocolAnalysis { enhanceProtocolRegistration(enhanceList); enhanceProtocolAfter(generateOperation); } catch (Exception e) { + e.printStackTrace(); throw new UnknownException(e); } } @@ -377,15 +379,16 @@ public class ProtocolAnalysis { GenerateProtobufUtils.clear(); } - private static List customFieldOrder(Class clazz) { + private static Entry, List> customFieldOrder(Class clazz) { var notCompatibleFields = new ArrayList(); var compatibleFieldMap = new HashMap(); + List originalFields = new ArrayList<>(); for (var field : clazz.getDeclaredFields()) { var modifiers = field.getModifiers(); if (Modifier.isTransient(modifiers) || Modifier.isStatic(modifiers)) { continue; } - if (Modifier.isFinal(modifiers)) { + if (!clazz.isRecord() && Modifier.isFinal(modifiers)) { throw new RunException("[{}]协议号中的[field:{}]属性的访问修饰符不能为final", clazz.getCanonicalName(), field.getName()); } if (!Modifier.isPublic(modifiers) && !Modifier.isPrivate(modifiers)) { @@ -400,6 +403,7 @@ public class ProtocolAnalysis { throw new RunException("[{}]协议号中的[field:{}]和[field:{}]不能有相同的Compatible顺序[order:{}]", clazz.getCanonicalName(), oldField.getName(), field.getName(), oldField, order); } } else { + originalFields.add(field); notCompatibleFields.add(field); } } @@ -415,26 +419,37 @@ public class ProtocolAnalysis { .map(Map.Entry::getValue) .toList(); notCompatibleFields.addAll(compatibleFields); - return notCompatibleFields; + return Map.entry(notCompatibleFields, originalFields); } private static ProtocolRegistration parseProtocolRegistration(Class clazz, ProtocolModule module) { var protocolId = ProtocolManager.protocolId(clazz); // 对象需要被序列化的属性 - var fields = customFieldOrder(clazz); + var fieldsEntry = customFieldOrder(clazz); try { var registrationList = new ArrayList(); + List fields = fieldsEntry.getKey(); + if (clazz.isRecord()) { + fields = fieldsEntry.getValue(); + } for (var field : fields) { registrationList.add(toRegistration(clazz, field)); } - var constructor = clazz.getDeclaredConstructor(); + Constructor constructor; + if (clazz.isRecord()) { + constructor = ReflectionUtils.getConstructor(clazz, fields.stream().map(p -> p.getType()).toList().toArray(new Class[]{})); + } else { + constructor = clazz.getDeclaredConstructor(); + } + ReflectionUtils.makeAccessible(constructor); var protocol = new ProtocolRegistration(); protocol.setId(protocolId); protocol.setConstructor(constructor); - protocol.setFields(ArrayUtils.listToArray(fields, Field.class)); + protocol.setFields(ArrayUtils.listToArray(fieldsEntry.getKey(), Field.class)); + protocol.setOriginalFields(ArrayUtils.listToArray(fieldsEntry.getValue(), Field.class)); protocol.setFieldRegistrations(ArrayUtils.listToArray(registrationList, IFieldRegistration.class)); protocol.setModule(module.getId()); return protocol; @@ -630,8 +645,10 @@ public class ProtocolAnalysis { // 不能是泛型类 AssertionUtils.isTrue(ArrayUtils.isEmpty(clazz.getTypeParameters()), "[class:{}]不能是泛型类", clazz.getCanonicalName()); - // 必须要有一个空的构造器 - Constructor constructor = ReflectionUtils.publicEmptyConstructor(clazz); + // 普通Pojo必须要有一个空的构造器 + if (!clazz.isRecord()) { + Constructor constructor = ReflectionUtils.publicEmptyConstructor(clazz); + } var protocolAnnotation = clazz.getDeclaredAnnotation(Protocol.class); short protocolId = -1; diff --git a/protocol/src/main/java/com/zfoo/protocol/registration/ProtocolRegistration.java b/protocol/src/main/java/com/zfoo/protocol/registration/ProtocolRegistration.java index c8e2238f..c8ce7cc6 100644 --- a/protocol/src/main/java/com/zfoo/protocol/registration/ProtocolRegistration.java +++ b/protocol/src/main/java/com/zfoo/protocol/registration/ProtocolRegistration.java @@ -38,6 +38,8 @@ public class ProtocolRegistration implements IProtocolRegistration { */ private IFieldRegistration[] fieldRegistrations; + private Field[] originalFields; + public ProtocolRegistration() { } @@ -122,6 +124,14 @@ public class ProtocolRegistration implements IProtocolRegistration { this.fields = fields; } + public Field[] getOriginalFields() { + return originalFields; + } + + public void setOriginalFields(Field[] originalFields) { + this.originalFields = originalFields; + } + public IFieldRegistration[] getFieldRegistrations() { return fieldRegistrations; } diff --git a/protocol/src/main/java/com/zfoo/protocol/util/ClassUtils.java b/protocol/src/main/java/com/zfoo/protocol/util/ClassUtils.java index 3a98a64e..e27b808e 100644 --- a/protocol/src/main/java/com/zfoo/protocol/util/ClassUtils.java +++ b/protocol/src/main/java/com/zfoo/protocol/util/ClassUtils.java @@ -393,8 +393,10 @@ public abstract class ClassUtils { ReflectionUtils.assertIsPojoClass(clazz); // 不能是泛型类 AssertionUtils.isTrue(ArrayUtils.isEmpty(clazz.getTypeParameters()), "[class:{}]不能是泛型类", clazz.getCanonicalName()); - // 必须要有一个空的构造器 - ReflectionUtils.publicEmptyConstructor(clazz); + // 普通Pojo必须要有一个空的构造器 + if (!clazz.isRecord()) { + ReflectionUtils.publicEmptyConstructor(clazz); + } var filedList = ReflectionUtils.notStaticAndTransientFields(clazz); diff --git a/protocol/src/main/java/com/zfoo/protocol/util/ReflectionUtils.java b/protocol/src/main/java/com/zfoo/protocol/util/ReflectionUtils.java index fc628cca..4cc8296b 100644 --- a/protocol/src/main/java/com/zfoo/protocol/util/ReflectionUtils.java +++ b/protocol/src/main/java/com/zfoo/protocol/util/ReflectionUtils.java @@ -75,7 +75,7 @@ public abstract class ReflectionUtils { } public static boolean isPojoClass(Class clazz) { - return clazz.getSuperclass().equals(Object.class); + return clazz.getSuperclass().equals(Object.class) || clazz.isRecord(); } public static void assertIsPojoClass(Class clazz) { @@ -322,11 +322,14 @@ public abstract class ReflectionUtils { public static String fieldToGetMethod(Class clazz, Field field) { var fieldName = field.getName(); - assertIsStandardFieldName(field); var methodName = "get" + StringUtils.capitalize(fieldName); + if (clazz.isRecord()) { + methodName = fieldName; + } + try { clazz.getDeclaredMethod(methodName); return methodName; @@ -358,6 +361,11 @@ public abstract class ReflectionUtils { assertIsStandardFieldName(field); var methodName = "set" + StringUtils.capitalize(fieldName); + + if (clazz.isRecord()) { + return methodName; + } + try { clazz.getDeclaredMethod(methodName, field.getType()); return methodName; diff --git a/storage/src/test/java/com/zfoo/storage/export/ExportBinaryTesting.java b/storage/src/test/java/com/zfoo/storage/export/ExportBinaryTesting.java index c3f558a8..e5e197f3 100644 --- a/storage/src/test/java/com/zfoo/storage/export/ExportBinaryTesting.java +++ b/storage/src/test/java/com/zfoo/storage/export/ExportBinaryTesting.java @@ -12,6 +12,7 @@ package com.zfoo.storage.export; +import com.zfoo.protocol.ProtocolManager; import com.zfoo.protocol.buffer.ByteBufUtils; import com.zfoo.protocol.generate.GenerateOperation; import com.zfoo.protocol.serializer.CodeLanguage; @@ -102,12 +103,12 @@ public class ExportBinaryTesting { var operation = new GenerateOperation(); operation.setProtocolPath("D:\\github\\godot-bird\\test\\storage\\protocol"); operation.getGenerateLanguages().add(CodeLanguage.GdScript); - //ProtocolManager.initProtocolAuto(protocols, operation); + ProtocolManager.initProtocolAuto(protocols, operation); // 生成数据 var resourceData = ExportUtils.autoWrapData(ResourceData.class, storageManager.storageMap()); var buffer = new UnpooledHeapByteBuf(ByteBufAllocator.DEFAULT, 100, Integer.MAX_VALUE); - //ProtocolManager.write(buffer, resourceData); + ProtocolManager.write(buffer, resourceData); var bytes = ByteBufUtils.readAllBytes(buffer); FileUtils.writeInputStreamToFile(new File("D:/github/godot-bird/binary_data.cfg"), new ByteArrayInputStream(bytes));