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;
+ }
+
/**
* 解析字段上的注解
*