From efae7d179a09bfa6d474246568c1b4ea95e241bd Mon Sep 17 00:00:00 2001 From: fengshuonan Date: Sat, 15 Jan 2022 18:26:28 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=907.1.6=E3=80=91=E3=80=90scanner?= =?UTF-8?q?=E3=80=91=E8=B0=83=E6=95=B4=E8=B5=84=E6=BA=90=E6=89=AB=E6=8F=8F?= =?UTF-8?q?=E8=BF=87=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scanner/api/context/MetadataContext.java | 100 ++++++++++++++++++ .../factory/ClassDetailMetadataFactory.java | 51 ++++++--- .../api/factory/ClassMetadataFactory.java | 13 +-- .../ClassDescriptionUtil.java | 2 +- .../FieldDescriptionUtil.java | 29 +++-- 5 files changed, 166 insertions(+), 29 deletions(-) create mode 100644 kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/context/MetadataContext.java rename kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/{factory/description => util}/ClassDescriptionUtil.java (96%) rename kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/{factory/description => util}/FieldDescriptionUtil.java (79%) diff --git a/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/context/MetadataContext.java b/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/context/MetadataContext.java new file mode 100644 index 000000000..e43d8e82d --- /dev/null +++ b/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/context/MetadataContext.java @@ -0,0 +1,100 @@ +package cn.stylefeng.roses.kernel.scanner.api.context; + +import cn.stylefeng.roses.kernel.scanner.api.enums.FieldTypeEnum; +import cn.stylefeng.roses.kernel.scanner.api.util.ClassTypeUtil; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 字段处理时的当前上下文 + *

+ * 记录每次的解析类的元数据时,处理过哪些实体类型,防止解析字段过程中的无限递归解析实体 + * + * @author fengshuonan + * @date 2022/1/15 10:36 + */ +public class MetadataContext { + + /** + * 第一个key是唯一id,用来标识针对某一次的类元数据解析 + */ + public static ConcurrentHashMap> META_DATA_CLASS_COUNT_CONTEXT = new ConcurrentHashMap<>(); + + /** + * 添加对某次解析的类记录 + * + * @author fengshuonan + * @date 2022/1/15 10:47 + */ + public static void addClassRecord(String uuid, String classPathName) { + Set classRecords = META_DATA_CLASS_COUNT_CONTEXT.get(uuid); + if (classRecords == null) { + classRecords = new HashSet<>(); + } + classRecords.add(classPathName); + META_DATA_CLASS_COUNT_CONTEXT.put(uuid, classRecords); + } + + /** + * 判断某个类是否已经被解析过 + * + * @author fengshuonan + * @date 2022/1/15 10:50 + */ + public static boolean ensureFieldClassHaveParse(String uuid, String classPathName) { + Set classRecords = META_DATA_CLASS_COUNT_CONTEXT.get(uuid); + if (classRecords == null) { + return false; + } + return classRecords.contains(classPathName); + } + + /** + * 判断某个类是否已经被解析过 + * + * @author fengshuonan + * @date 2022/1/15 10:50 + */ + public static boolean ensureFieldClassHaveParse(String uuid, Type genericType) { + Set classRecords = META_DATA_CLASS_COUNT_CONTEXT.get(uuid); + if (classRecords != null) { + // 获取字段类型,如果是数组,collection带实体的,需要获取真实的实体类型 + FieldTypeEnum classFieldType = ClassTypeUtil.getClassFieldType(genericType); + + // 如果是对象类型,直接判断 + if (classFieldType.equals(FieldTypeEnum.OBJECT)) { + return classRecords.contains(((Class) genericType).getName()); + } + + // 数组类型,则获取数组的实体 + if (classFieldType.equals(FieldTypeEnum.ARRAY_WITH_OBJECT)) { + Class originClass = (Class) genericType; + return classRecords.contains(originClass.getComponentType().getName()); + } + + // 集合类型,获取集合的真实类型 + if (classFieldType.equals(FieldTypeEnum.COLLECTION_WITH_OBJECT)) { + // 获取泛型 + ParameterizedType parameterizedType = (ParameterizedType) genericType; + Type actualTypeArgument = parameterizedType.getActualTypeArguments()[0]; + return ensureFieldClassHaveParse(uuid, actualTypeArgument); + } + } + return false; + } + + /** + * 清空当前解析的记录 + * + * @author fengshuonan + * @date 2022/1/15 10:49 + */ + public static void cleanContext() { + META_DATA_CLASS_COUNT_CONTEXT.clear(); + } + +} diff --git a/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/factory/ClassDetailMetadataFactory.java b/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/factory/ClassDetailMetadataFactory.java index 5cf71d59e..711c2fef7 100644 --- a/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/factory/ClassDetailMetadataFactory.java +++ b/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/factory/ClassDetailMetadataFactory.java @@ -1,10 +1,12 @@ package cn.stylefeng.roses.kernel.scanner.api.factory; +import cn.hutool.core.util.ObjectUtil; +import cn.stylefeng.roses.kernel.scanner.api.context.MetadataContext; import cn.stylefeng.roses.kernel.scanner.api.enums.FieldTypeEnum; -import cn.stylefeng.roses.kernel.scanner.api.factory.description.ClassDescriptionUtil; -import cn.stylefeng.roses.kernel.scanner.api.factory.description.FieldDescriptionUtil; import cn.stylefeng.roses.kernel.scanner.api.pojo.resource.FieldMetadata; +import cn.stylefeng.roses.kernel.scanner.api.util.ClassDescriptionUtil; import cn.stylefeng.roses.kernel.scanner.api.util.ClassTypeUtil; +import cn.stylefeng.roses.kernel.scanner.api.util.FieldDescriptionUtil; import lombok.extern.slf4j.Slf4j; import java.lang.reflect.Field; @@ -25,10 +27,12 @@ public class ClassDetailMetadataFactory { /** * 根据传入的类型,解析出这个类型的所有子字段类型 * + * @param fieldType 需要被解析的对象的类型,可以是class也可以是泛型 + * @param uuid 随机字符串,保证唯一性,用来标识从开始到结束一个context周期内的一系列解析 * @author fengshuonan * @date 2022/1/14 14:31 */ - public static Set createFieldDetailMetadataSet(Type fieldType) { + public static Set createFieldDetailMetadataSet(Type fieldType, String uuid) { // 获取参数的类型枚举 FieldTypeEnum classFieldType = ClassTypeUtil.getClassFieldType(fieldType); @@ -54,7 +58,7 @@ public class ClassDetailMetadataFactory { // 如果是带实体的数组,则加上实体对应的字段信息 Class objArrayClass = (Class) fieldType; Class objArrayComponentType = objArrayClass.getComponentType(); - fieldMetadata = ClassDetailMetadataFactory.createFieldDetailMetadataSet(objArrayComponentType); + fieldMetadata = ClassDetailMetadataFactory.createFieldDetailMetadataSet(objArrayComponentType, uuid); break; case BASE_COLLECTION: // 如果是基础集合,因为不确定集合的内容,所以使用Object来描述一下集合的具体内容 @@ -66,16 +70,24 @@ public class ClassDetailMetadataFactory { // 如果是集合里带的具体实体对象,则描述一下具体实体的数据结构 ParameterizedType collectionParameterizedType = (ParameterizedType) fieldType; Type[] actualTypeArguments = collectionParameterizedType.getActualTypeArguments(); - fieldMetadata = ClassDetailMetadataFactory.createFieldDetailMetadataSet(actualTypeArguments[0]); + fieldMetadata = ClassDetailMetadataFactory.createFieldDetailMetadataSet(actualTypeArguments[0], uuid); break; case OBJECT: // 如果是实体对象,则描述实体对象的所有字段信息 Class objectClass = (Class) fieldType; Field[] fields = objectClass.getDeclaredFields(); fieldMetadata = new LinkedHashSet<>(); + + // 在处理Object中所有字段之前,将当前父类放进context,所有子字段不能含有父类的类型,否则会递归 + MetadataContext.addClassRecord(uuid, objectClass.getName()); + for (Field field : fields) { - // 获取字段的具体属性 - FieldMetadata fieldInfo = FieldDescriptionUtil.createFieldMetadata(field); + FieldMetadata fieldInfo; + if (MetadataContext.ensureFieldClassHaveParse(uuid, field.getGenericType())) { + fieldInfo = FieldDescriptionUtil.createBasicMetadata(field, uuid); + } else { + fieldInfo = FieldDescriptionUtil.createFieldMetadata(field, uuid); + } fieldMetadata.add(fieldInfo); } break; @@ -86,8 +98,16 @@ public class ClassDetailMetadataFactory { Type rawType = objWithGenericParameterizedType.getRawType(); // 获取具体泛型 Type genericType = objWithGenericParameterizedType.getActualTypeArguments()[0]; + + // 判断带泛型的实体,有没有进行做字段解析,如果解析过,则跳过 + String totalName = fieldType.getTypeName() + genericType.getTypeName(); + if (MetadataContext.ensureFieldClassHaveParse(uuid, totalName)) { + return null; + } + MetadataContext.addClassRecord(uuid, totalName); + // 获取主体的所有字段信息 - fieldMetadata = getEntityWithGenericFieldMetadataList(rawType, genericType); + fieldMetadata = getEntityWithGenericFieldMetadataList(rawType, genericType, uuid); default: } @@ -95,22 +115,25 @@ public class ClassDetailMetadataFactory { } /** - * 获取实体带泛型类型的字段填充详情 + * 获取实体带泛型类型的字段填充详情,例如PageResult这种字段 * * @author fengshuonan * @date 2022/1/14 18:51 */ - public static Set getEntityWithGenericFieldMetadataList(Type fieldType, Type genericType) { + public static Set getEntityWithGenericFieldMetadataList(Type fieldType, Type genericType, String uuid) { if (fieldType instanceof Class) { Class clazz = (Class) fieldType; - // 获取主类型的所有带泛型的属性 - Set fieldDetailMetadataSet = createFieldDetailMetadataSet(clazz); + // 获取主类型的所有属性 + Set fieldDetailMetadataSet = createFieldDetailMetadataSet(clazz, uuid); + if (ObjectUtil.isEmpty(fieldDetailMetadataSet)) { + return null; + } for (FieldMetadata fieldMetadata : fieldDetailMetadataSet) { // 如果是带泛型集合,如下情况List,又或是直接 T 这种形式 if (FieldTypeEnum.COLLECTION_WITH_OBJECT.getCode().equals(fieldMetadata.getFieldType()) || FieldTypeEnum.WITH_UNKNOWN_GENERIC.getCode().equals(fieldMetadata.getFieldType())) { // 设置这个字段的子字段描述 - fieldMetadata.setGenericFieldMetadata(createFieldDetailMetadataSet(genericType)); + fieldMetadata.setGenericFieldMetadata(createFieldDetailMetadataSet(genericType, uuid)); } // 如果T在携带在一个实体类上,例如ResponseData这种形式 @@ -118,7 +141,7 @@ public class ClassDetailMetadataFactory { // 设置这个字段的子字段描述 Set current = null; try { - current = getEntityWithGenericFieldMetadataList(Class.forName(fieldMetadata.getFieldClassPath()), genericType); + current = getEntityWithGenericFieldMetadataList(Class.forName(fieldMetadata.getFieldClassPath()), genericType, uuid); } catch (ClassNotFoundException e) { log.error("类无法找到" + fieldMetadata.getFieldClassPath(), e); continue; diff --git a/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/factory/ClassMetadataFactory.java b/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/factory/ClassMetadataFactory.java index 5f2bfd088..97a85ec31 100644 --- a/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/factory/ClassMetadataFactory.java +++ b/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/factory/ClassMetadataFactory.java @@ -1,8 +1,8 @@ package cn.stylefeng.roses.kernel.scanner.api.factory; import cn.stylefeng.roses.kernel.scanner.api.enums.FieldTypeEnum; -import cn.stylefeng.roses.kernel.scanner.api.factory.description.ClassDescriptionUtil; import cn.stylefeng.roses.kernel.scanner.api.pojo.resource.FieldMetadata; +import cn.stylefeng.roses.kernel.scanner.api.util.ClassDescriptionUtil; import cn.stylefeng.roses.kernel.scanner.api.util.ClassTypeUtil; import lombok.extern.slf4j.Slf4j; @@ -11,7 +11,7 @@ import java.lang.reflect.Type; import java.util.Set; /** - * 字段信息创建工具 + * 字段信息创建工具,一般用这个类作为类解析的入口 * * @author fengshuonan * @date 2022/1/13 13:49 @@ -22,12 +22,13 @@ public class ClassMetadataFactory { /** * 通过传入的类型(Class或ParameterizedType)进行字段校验,解析出字段的元数据 * - * @param type 类型 + * @param type 需要被解析的对象的类型,可以是class也可以是泛型 + * @param uuid 随机字符串,保证唯一性,用来标识从开始到结束一个context周期内的一系列解析 * @return 传入类型的字段元数据信息 * @author fengshuonan * @date 2022/1/13 13:51 */ - public static FieldMetadata beginCreateFieldMetadata(Type type) { + public static FieldMetadata beginCreateFieldMetadata(Type type, String uuid) { // 获取类型的枚举 FieldTypeEnum classFieldType = ClassTypeUtil.getClassFieldType(type); @@ -43,7 +44,7 @@ public class ClassMetadataFactory { fieldMetadata = ClassDescriptionUtil.createClassMetadata(clazz, classFieldType); // 补充类型的子信息 - Set fieldDetailMetadataSet = ClassDetailMetadataFactory.createFieldDetailMetadataSet(clazz); + Set fieldDetailMetadataSet = ClassDetailMetadataFactory.createFieldDetailMetadataSet(clazz, uuid); fieldMetadata.setGenericFieldMetadata(fieldDetailMetadataSet); } @@ -55,7 +56,7 @@ public class ClassMetadataFactory { FieldMetadata baseMetadata = ClassDescriptionUtil.createParameterizedMetadata(parameterizedType, classFieldType); // 补充类型的子信息 - Set fieldDetailMetadataSet = ClassDetailMetadataFactory.createFieldDetailMetadataSet(type); + Set fieldDetailMetadataSet = ClassDetailMetadataFactory.createFieldDetailMetadataSet(type, uuid); baseMetadata.setGenericFieldMetadata(fieldDetailMetadataSet); } diff --git a/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/factory/description/ClassDescriptionUtil.java b/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/util/ClassDescriptionUtil.java similarity index 96% rename from kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/factory/description/ClassDescriptionUtil.java rename to kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/util/ClassDescriptionUtil.java index 96ce1c0a0..565b2baaf 100644 --- a/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/factory/description/ClassDescriptionUtil.java +++ b/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/util/ClassDescriptionUtil.java @@ -1,4 +1,4 @@ -package cn.stylefeng.roses.kernel.scanner.api.factory.description; +package cn.stylefeng.roses.kernel.scanner.api.util; import cn.hutool.core.util.IdUtil; import cn.stylefeng.roses.kernel.scanner.api.enums.FieldMetadataTypeEnum; diff --git a/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/factory/description/FieldDescriptionUtil.java b/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/util/FieldDescriptionUtil.java similarity index 79% rename from kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/factory/description/FieldDescriptionUtil.java rename to kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/util/FieldDescriptionUtil.java index 378942d76..c41c5ee66 100644 --- a/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/factory/description/FieldDescriptionUtil.java +++ b/kernel-d-scanner/scanner-api/src/main/java/cn/stylefeng/roses/kernel/scanner/api/util/FieldDescriptionUtil.java @@ -1,4 +1,4 @@ -package cn.stylefeng.roses.kernel.scanner.api.factory.description; +package cn.stylefeng.roses.kernel.scanner.api.util; import cn.hutool.core.util.IdUtil; import cn.stylefeng.roses.kernel.rule.annotation.ChineseDescription; @@ -6,8 +6,6 @@ import cn.stylefeng.roses.kernel.scanner.api.enums.FieldMetadataTypeEnum; import cn.stylefeng.roses.kernel.scanner.api.enums.FieldTypeEnum; import cn.stylefeng.roses.kernel.scanner.api.factory.ClassDetailMetadataFactory; import cn.stylefeng.roses.kernel.scanner.api.pojo.resource.FieldMetadata; -import cn.stylefeng.roses.kernel.scanner.api.util.ClassReflectUtil; -import cn.stylefeng.roses.kernel.scanner.api.util.ClassTypeUtil; import java.lang.annotation.Annotation; import java.lang.reflect.Field; @@ -26,12 +24,12 @@ import java.util.Set; public class FieldDescriptionUtil { /** - * 创建类内字段的元数据 + * 创建类内字段的元数据,只组装基本信息 * * @author fengshuonan * @date 2022/1/13 18:06 */ - public static FieldMetadata createFieldMetadata(Field field) { + public static FieldMetadata createBasicMetadata(Field field, String uuid) { FieldMetadata fieldMetadataItem = new FieldMetadata(); // 设置唯一id fieldMetadataItem.setMetadataId(IdUtil.fastSimpleUUID()); @@ -59,12 +57,27 @@ public class FieldDescriptionUtil { // 设置字段类型,基本、数组、还是object FieldTypeEnum classFieldType = ClassTypeUtil.getClassFieldType(genericType); fieldMetadataItem.setFieldType(classFieldType.getCode()); - // 根据情况,获取字段的具体子数据描述 - Set fieldDetailMetadataSet = ClassDetailMetadataFactory.createFieldDetailMetadataSet(genericType); - fieldMetadataItem.setGenericFieldMetadata(fieldDetailMetadataSet); return fieldMetadataItem; } + /** + * 创建类内字段的元数据,组装基本信息 + 子字段信息 + *

+ * 为何区分两个方法分别组装,因为存在实体中又包含本实体字段的情况,会出现无限递归 + * + * @author fengshuonan + * @date 2022/1/13 18:06 + */ + public static FieldMetadata createFieldMetadata(Field field, String uuid) { + // 先组装基础数据 + FieldMetadata fieldMetadata = createBasicMetadata(field, uuid); + // 组装子类型数据 + Type genericType = field.getGenericType(); + Set fieldDetailMetadataSet = ClassDetailMetadataFactory.createFieldDetailMetadataSet(genericType, uuid); + fieldMetadata.setGenericFieldMetadata(fieldDetailMetadataSet); + return fieldMetadata; + } + /** * 解析字段上的注解 *