mirror of https://gitee.com/stylefeng/roses
【7.1.6】【scanner】调整资源扫描过程
parent
b0a72d5ebf
commit
efae7d179a
|
@ -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;
|
||||
|
||||
/**
|
||||
* 字段处理时的当前上下文
|
||||
* <p>
|
||||
* 记录每次的解析类的元数据时,处理过哪些实体类型,防止解析字段过程中的无限递归解析实体
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2022/1/15 10:36
|
||||
*/
|
||||
public class MetadataContext {
|
||||
|
||||
/**
|
||||
* 第一个key是唯一id,用来标识针对某一次的类元数据解析
|
||||
*/
|
||||
public static ConcurrentHashMap<String, Set<String>> META_DATA_CLASS_COUNT_CONTEXT = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 添加对某次解析的类记录
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2022/1/15 10:47
|
||||
*/
|
||||
public static void addClassRecord(String uuid, String classPathName) {
|
||||
Set<String> 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<String> 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<String> 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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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<FieldMetadata> createFieldDetailMetadataSet(Type fieldType) {
|
||||
public static Set<FieldMetadata> 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<SysUser>这种字段
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2022/1/14 18:51
|
||||
*/
|
||||
public static Set<FieldMetadata> getEntityWithGenericFieldMetadataList(Type fieldType, Type genericType) {
|
||||
public static Set<FieldMetadata> getEntityWithGenericFieldMetadataList(Type fieldType, Type genericType, String uuid) {
|
||||
if (fieldType instanceof Class<?>) {
|
||||
Class<?> clazz = (Class<?>) fieldType;
|
||||
// 获取主类型的所有带泛型的属性
|
||||
Set<FieldMetadata> fieldDetailMetadataSet = createFieldDetailMetadataSet(clazz);
|
||||
// 获取主类型的所有属性
|
||||
Set<FieldMetadata> fieldDetailMetadataSet = createFieldDetailMetadataSet(clazz, uuid);
|
||||
if (ObjectUtil.isEmpty(fieldDetailMetadataSet)) {
|
||||
return null;
|
||||
}
|
||||
for (FieldMetadata fieldMetadata : fieldDetailMetadataSet) {
|
||||
// 如果是带泛型集合,如下情况List<T>,又或是直接 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<T>这种形式
|
||||
|
@ -118,7 +141,7 @@ public class ClassDetailMetadataFactory {
|
|||
// 设置这个字段的子字段描述
|
||||
Set<FieldMetadata> 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;
|
||||
|
|
|
@ -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<FieldMetadata> fieldDetailMetadataSet = ClassDetailMetadataFactory.createFieldDetailMetadataSet(clazz);
|
||||
Set<FieldMetadata> fieldDetailMetadataSet = ClassDetailMetadataFactory.createFieldDetailMetadataSet(clazz, uuid);
|
||||
fieldMetadata.setGenericFieldMetadata(fieldDetailMetadataSet);
|
||||
}
|
||||
|
||||
|
@ -55,7 +56,7 @@ public class ClassMetadataFactory {
|
|||
FieldMetadata baseMetadata = ClassDescriptionUtil.createParameterizedMetadata(parameterizedType, classFieldType);
|
||||
|
||||
// 补充类型的子信息
|
||||
Set<FieldMetadata> fieldDetailMetadataSet = ClassDetailMetadataFactory.createFieldDetailMetadataSet(type);
|
||||
Set<FieldMetadata> fieldDetailMetadataSet = ClassDetailMetadataFactory.createFieldDetailMetadataSet(type, uuid);
|
||||
baseMetadata.setGenericFieldMetadata(fieldDetailMetadataSet);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
|
@ -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<FieldMetadata> fieldDetailMetadataSet = ClassDetailMetadataFactory.createFieldDetailMetadataSet(genericType);
|
||||
fieldMetadataItem.setGenericFieldMetadata(fieldDetailMetadataSet);
|
||||
return fieldMetadataItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建类内字段的元数据,组装基本信息 + 子字段信息
|
||||
* <p>
|
||||
* 为何区分两个方法分别组装,因为存在实体中又包含本实体字段的情况,会出现无限递归
|
||||
*
|
||||
* @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<FieldMetadata> fieldDetailMetadataSet = ClassDetailMetadataFactory.createFieldDetailMetadataSet(genericType, uuid);
|
||||
fieldMetadata.setGenericFieldMetadata(fieldDetailMetadataSet);
|
||||
return fieldMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析字段上的注解
|
||||
*
|
Loading…
Reference in New Issue