feat[class]: 增加获取类的相关类的relevantClass方法

This commit is contained in:
godotg committed 2022-10-26 15:32:43 +08:00
1 parent 3e29343393
commit 248faeec13
2 files changed
+158 -6

No files matched your search

@@ -12,9 +12,17 @@
package com.zfoo.protocol.util;
import com.zfoo.protocol.collection.ArrayUtils;
import com.zfoo.protocol.exception.RunException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author godotg
@@ -22,6 +30,15 @@ import java.io.InputStream;
*/
public abstract class ClassUtils {
/**
* 从类路径中读取文件
*
* @param filePath 一般指resources中的文件,也可以在jar中
*/
public static InputStream getFileFromClassPath(String filePath) throws IOException {
return getDefaultClassLoader().getResource(filePath).openStream();
}
/**
* 获取编译过后的类文件(*.class)的绝对路径
*
@@ -33,7 +50,6 @@ public abstract class ClassUtils {
return file.getAbsolutePath();
}
public static ClassLoader getDefaultClassLoader() {
var classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader != null) {
@@ -53,12 +69,139 @@ public abstract class ClassUtils {
/**
* 从类路径中读取文件
*
* @param filePath 一般指resources中的文件,也可以在jar中
* 返回和clazz相关的所有子协议,协议和protocol协议一致
*/
public static InputStream getFileFromClassPath(String filePath) throws IOException {
return getDefaultClassLoader().getResource(filePath).openStream();
public static Set<Class<?>> relevantClass(Class<?> clazz) {
var classSet = new HashSet<Class<?>>();
return relevantClass(clazz, classSet);
}
private static Set<Class<?>> relevantClass(Class<?> clazz, Set<Class<?>> classSet) {
if (!classSet.add(clazz)) {
return classSet;
}
// 是否为一个简单的javabean,为了防止不同层对象混用造成潜在的并发问题,特别是网络层和po层混用
ReflectionUtils.assertIsPojoClass(clazz);
// 不能是泛型类
AssertionUtils.isTrue(ArrayUtils.isEmpty(clazz.getTypeParameters()), "[class:{}]不能是泛型类", clazz.getCanonicalName());
// 必须要有一个空的构造器
ReflectionUtils.publicEmptyConstructor(clazz);
var filedList = Arrays.stream(clazz.getDeclaredFields())
.filter(it -> !Modifier.isTransient(it.getModifiers()))
.filter(it -> !Modifier.isStatic(it.getModifiers()))
.collect(Collectors.toList());
for (var field : filedList) {
// entity必须包含属性的get和set方法
ReflectionUtils.fieldToGetMethod(clazz, field);
ReflectionUtils.fieldToSetMethod(clazz, field);
// 是一个基本类型变量
var fieldType = field.getType();
if (isBaseType(fieldType)) {
// do nothing
} else if (fieldType.isArray()) {
// 是一个数组
Class<?> arrayClazz = fieldType.getComponentType();
relevantClass0(arrayClazz, classSet);
} else if (Set.class.isAssignableFrom(fieldType)) {
AssertionUtils.isTrue(fieldType.equals(Set.class), "[class:{}]类型声明不正确,必须是Set接口类型", clazz.getCanonicalName());
var type = field.getGenericType();
AssertionUtils.isTrue(type instanceof ParameterizedType, "[class:{}]类型声明不正确,不是泛型类[field:{}]", clazz.getCanonicalName(), field.getName());
var types = ((ParameterizedType) type).getActualTypeArguments();
AssertionUtils.isTrue(types.length == 1, "[class:{}]中Set类型声明不正确,[field:{}]必须声明泛型类", clazz.getCanonicalName(), field.getName());
relevantClass0(types[0], classSet);
} else if (List.class.isAssignableFrom(fieldType)) {
// 是一个List
AssertionUtils.isTrue(fieldType.equals(List.class), "[class:{}]类型声明不正确,必须是List接口类型", clazz.getCanonicalName());
var type = field.getGenericType();
AssertionUtils.isTrue(type instanceof ParameterizedType, "[class:{}]类型声明不正确,不是泛型类[field:{}]", clazz.getCanonicalName(), field.getName());
var types = ((ParameterizedType) type).getActualTypeArguments();
AssertionUtils.isTrue(types.length == 1, "[class:{}]中List类型声明不正确,[field:{}]必须声明泛型类", clazz.getCanonicalName(), field.getName());
relevantClass0(types[0], classSet);
} else if (Map.class.isAssignableFrom(fieldType)) {
if (!fieldType.equals(Map.class)) {
throw new RunException("[class:{}]类型声明不正确,必须是Map接口类型", clazz.getCanonicalName());
}
var type = field.getGenericType();
if (!(type instanceof ParameterizedType)) {
throw new RunException("[class:{}]中数组类型声明不正确,[field:{}]不是泛型类", clazz.getCanonicalName(), field.getName());
}
var types = ((ParameterizedType) type).getActualTypeArguments();
if (types.length != 2) {
throw new RunException("[class:{}]中数组类型声明不正确,[field:{}]必须声明泛型类", clazz.getCanonicalName(), field.getName());
}
var keyType = types[0];
var valueType = types[1];
relevantClass0(keyType, classSet);
relevantClass0(valueType, classSet);
} else {
relevantClass(fieldType, classSet);
}
}
return classSet;
}
private static void relevantClass0(Type type, Set<Class<?>> classSet) {
if (type instanceof ParameterizedType) {
// 泛型类
Class<?> clazz = (Class<?>) ((ParameterizedType) type).getRawType();
if (Set.class.equals(clazz)) {
// Set<Set<String>>
relevantClass0(((ParameterizedType) type).getActualTypeArguments()[0], classSet);
return;
} else if (List.class.equals(clazz)) {
// List<List<String>>
relevantClass0(((ParameterizedType) type).getActualTypeArguments()[0], classSet);
return;
} else if (Map.class.equals(clazz)) {
// Map<List<String>, List<String>>
var types = ((ParameterizedType) type).getActualTypeArguments();
var keyType = types[0];
var valueType = types[1];
relevantClass0(keyType, classSet);
relevantClass0(valueType, classSet);
return;
}
} else if (type instanceof Class) {
Class<?> clazz = ((Class<?>) type);
if (isBaseType(clazz)) {
// do nothing
return;
} else if (clazz.getComponentType() != null) {
// 是一个二维以上数组
throw new RunException("不支持多维数组或集合嵌套数组[type:{}]类型,仅支持一维数组", type);
} else if (clazz.equals(List.class) || clazz.equals(Set.class) || clazz.equals(Map.class)) {
throw new RunException("不支持数组和集合联合使用[type:{}]类型", type);
} else {
if (!classSet.add(clazz)) {
return;
}
relevantClass(clazz, classSet);
return;
}
}
throw new RunException("[type:{}]类型不正确", type);
}
private static boolean isBaseType(Class<?> clazz) {
return clazz.isPrimitive()
|| Number.class.isAssignableFrom(clazz)
|| String.class.isAssignableFrom(clazz)
|| Boolean.class.isAssignableFrom(clazz)
|| Character.class.isAssignableFrom(clazz);
}
}
@@ -13,6 +13,7 @@
package com.zfoo.protocol.util;
import com.zfoo.protocol.packet.ComplexObject;
import org.junit.Ignore;
import org.junit.Test;
@@ -35,4 +36,12 @@ public class ClassUtilsTest {
System.out.println(new String(IOUtils.toByteArray(ClassUtils.getFileFromClassPath("csTest/CsProtocolTest.cs"))));
}
@Test
public void relevantClassTest() throws IOException {
var set = ClassUtils.relevantClass(ComplexObject.class);
for (var clazz : set) {
System.out.println(clazz);
}
}
}