java 对象数据空状态解析
1 背景简介
程序开发中经常会定义各种实体类并创建对应的对象来完成数据的封装及传递,使用时往往需要对 null 信息进行判断在大部分时候开发者会在使用时对需要的内部字段进行进行检查,代码冗杂且无复用性,尤其是在在传递各种嵌套json并生成实体类时往往会碰到。这类嵌套非常多的对象需要逐层进入检查(尤其是使用条件严苛的,每个内部字段及其嵌套对象都不允许为null时,需要做全盘检查),嵌套判断多,繁琐,枯燥无味但又不可避免。
如果不想使用 if 判断,也可以使用java Optional 类来避免实际项目中的null 值判断,该类提map供流式,在创建或者获取内部元素时可以使用ofNullable 或者 orElse可以规避null状态对后续代码的影响。但使用时依然会有频繁获取或者创建Optional的操作,代码结构不够优雅。可读性也不高。
因此,实现一种可复用的java 对象信息空状态分析工具,来完成这类繁琐,重复的工作。提高开发效率。
2 功能实现
2.1 需求分析
要求该工具类可以实现任意对象的内部字段null 状态判断。在开发设计上要考虑各种外部因素及其约束条件,尽量通用。包括但不限于:带有泛型声明的Map或、Collection或者任意用户自定义泛型类对象的子类对象及其内部所有添加的元素 ;外部第三方类对象或者自定义类对象(可包含泛型声明)及其嵌套内部对象,嵌套形式可以任意。
另外,检查反馈的信息包含以下几个
- 为空的变量名称,检查时会层层递归,在上层变量名称基础上添加变量声明链路。集合,数组 Map 需要在检查后返回null元素的下标位置。比如 bvariableCheckTestClass.data.resources(6).laneEles(4).crossCode;这种形式方便回溯
- 变量所属的类名称。
- 变量本身类型,如果是类型变量T ,通配泛型? extend T 或 ? super T 需要在对象状态为空时推断出实际类型。(null 情况无法实际使用对象的getClass 方法获取),如果不加这条需求,实现上可以简单不少,加上类型推断难度会增加。
- 定义变量的上级对象的地址
2.1 流程设计及代码实现
流程图如下:
其中比较难的点在于递归反射时存在各种泛型,为null 的字段对象的实际类型推断(不可直接使用对象的getClass方法)。
JDK原生API 中, java中class,method,field,construct的继承体系如下:
java中所有对象的类型定义类Type及其子类继承关系如下:
综上,反射分析类型Type主要包含5种子类。因此在解析时围绕这5中分析即可。
sun.reflect.generics.reflectiveObjects.WildcardTypeImpl
sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
java.lang.Class
sun.reflect.generics.reflectiveObjects.TypeVariableImpl
sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl
2.2代码实现
主要功能类及其关系如下。
VariableRecurisonCheck 是主要功能类,check 是检查对象数据信息的主要方法,ReflectUtil 是反射工具类,主要是反射并缓存类的字段信息、方法和构造器,一次反射,多次使用,提高效率。其他几个功能类实现统一接口,实现了不同Type 类型下的处理策略。所有代码请参见后文。
另外图中未列出的CheckNullInfo 类是检查信息封装实体类,CheckNullTag 是注解类,可标注哪些字段是否开启检查。
CheckNullInfo:
package com.shzz.commom.utils.dto;
import java.io.Serializable;
import java.util.*;
public class CheckNullInfo implements Cloneable, Serializable {
// 为空的变量名称
private String variableName;
// 变量所属的类
private String variableName;
// 变量 String declareClass="";
// 变量本身类型
private String variableName;
// 变量 String variableType="";
// 定义变量的上级对象的地址
private String variableName;
// 变量 String ownerObjectAddr="";
public String getDeclareClass() {
return declareClass;
}
public void setDeclareClass(String declareClass) {
this.declareClass = declareClass;
}
public String getVariableName() {
return variableName;
}
public void setVariableName(String variableName) {
this.variableName = variableName;
}
public String getVariableType() {
return variableType;
}
public void setVariableType(String variableType) {
this.variableType = variableType;
}
public String getOwnerObjectAddr() {
return ownerObjectAddr;
}
public void setOwnerObjectAddr(String ownerObjectAddr) {
this.ownerObjectAddr = ownerObjectAddr;
}
@Override
public String toString() {
return "CheckNullInfo{" +
"variableName='" + variableName + '\'' +
", variableType='" + variableType + '\'' +
", declareClass='" + declareClass + '\'' +
", ownerObjectAddr='" + ownerObjectAddr + '\'' +
'}';
}
}
CheckNullTag:
package com.shzz.commom.utils.annotation;
import java.lang.annotation.*;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckNullTag {
String description() default "";
boolean check() default true;
}
VariableRecurisonCheck 方法列表如下:
VariableRecurisonCheck 代码如下:
package com.shzz.commom.utils.reflect;
import com.shzz.commom.utils.dto.CheckNullInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.*;
/**
* @Classname VariableRecurisonCheck
* @Description TODO
* @Date 2021/1/19 17:50
* @Created by wenyiking
*/
public class VariableRecurisonCheck {
private static volatile VariableRecurisonCheck variableRecurisonCheck = new VariableRecurisonCheck();
private VariableRecurisonCheck() {
}
public static VariableRecurisonCheck getInstance() {
return variableRecurisonCheck;
}
private final static Logger LOG = LoggerFactory.getLogger(VariableRecurisonCheck.class);
protected static HashMap<String, GenericStrategy> genericStrategyHashMap = new HashMap<>(8);
static {
//泛型实际参数类型 WildCardType ParameterType TypeVariable GenericArrayType Class 处理策略缓存。
genericStrategyHashMap.put("sun.reflect.generics.reflectiveObjects.WildcardTypeImpl", new WildcardTypeProcess());
genericStrategyHashMap.put("sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl", new ParameterizedTypeProcess());
genericStrategyHashMap.put("java.lang.Class", new ClassTypeProcess());
genericStrategyHashMap.put("sun.reflect.generics.reflectiveObjects.TypeVariableImpl", new TypeVariableProcess());
genericStrategyHashMap.put("sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl", new GenericArrayTypeProcess());
}
public List<CheckNullInfo> checkSpecificField(HashMap<String, CheckNullInfo> checkNullInfoAll, String fieldName, String upperVariableName) {
/**
* @description: 查找嵌套类某个字段为空的对象信息,
* CheckNullInfo 封装的变量信息是链式的,出现空值时,记录的变量名是层从上层到下层依次将变量名称拼接,
* 比如:
* 'variableCheckTestClass.data.resources(6).laneEles(4).crossCode'
* 'variableCheckTestClass.data.resources(0).laneEles(10).crossCode'
* 'variableCheckTestClass.data.resources(11).laneEles(16).crossCode'
*
* 对于集合中的对象,如果对象的某个属性在多个对象中都出现空值,则基于相同的变量名和上层变量名查询时能够查找出多个空值对象。
* 比如,上面基于crossCode 和 laneEles 查询可以查到列表中crossCode 为null 的多个空对象
* @param checkNullInfoAll
* @param fieldName 字段名称
* @param upperVariableName 上层变量名称
* @return: java.util.List<com.zhzz.commom.utils.dto.CheckNullInfo>
* @auther: wenyiking
* @date: 2021/2/1 18:30
*/
List<CheckNullInfo> checkNullInfoList = new ArrayList<>();
Set<String> keySet = checkNullInfoAll.keySet();
for (String key : keySet) {
if (key.contains(fieldName) && key.contains(upperVariableName)) {
checkNullInfoList.add(checkNullInfoAll.get(key));
}
}
return checkNullInfoList;
}
public HashMap<String, CheckNullInfo> check(Object checkObject, Type selfType, Type declaringType, String variableName, Object ownerObject, Type[] genericArguments) throws Exception {
/**
* @description: 该函数是 检查对象及其嵌套对象空状态的入口函数。
* 功能描述:检查指定对象及其内部元素的空值状态,并返回所有空状态的元素信息,
* 可添加注解CheckNullTag 在字段上,如果不想检查内部某个字段指定 @CheckNullTag(check = false) 即可。不添加注解或者check=true 都默认要检查
* 检查返回的具体信息参见方法return 说明。参数说明参见 @param
* 任意对象范围:
* 1 方法局部变量中带有泛型声明的Map 或者Collection 的子类对象本身及其内部所有添加的元素
* 2 外部第三方类对象或者自定义类对象(可包含泛型声明)及其嵌套内部对象,嵌套形式可以多种。
* 该工具类类使用 时可直接 通过静态方法方法获取单例 VariableRecurisonCheck variableRecurisonCheck=VariableRecurisonCheck.getInstance();
* @param checkObject 待检查对象
* @param selfType 待检查对象的类型,Type 是Class GenericArrayType ParameterizedType WildcardType TypeVariable 顶层接口。
* 由于实际检查 的类型可能是以上任意一种,所以
* @param declaringType 声明待检查对象的上层类类型,如果是字段,上层类就是包含字段的那个类,
* 如果是局部变量,那么直接通过方法传入this 对象的类型即可
* @param variableName 待检查的变量名称,检查时会层层递归,在上层变量名称基础上添加变量声明链路。
* @param ownerObject 定义待检查对象的上层对象。这个是为了后续定位内层空对象时找到上层对象信息
* @param genericArguments 泛型参数,如果局部变量本身就是泛型相关的类型,则调用函数时直接指定,
* 比如某个方法局部变量如下:
* HashMap<String,String> testHashMap=new HashMap<String,String>(){};
*
* testHashMap.put("first","first");
* testHashMap.put("second",null);
* testHashMap.put("third","third");
* 检查testHashMap 信息时, Type[] genericArguments={String.class,String.class}; 按照泛型声明的顺序添加具体泛型类型即可。
* (备注,这个示例有一定特殊性,除了手动指定,其实还有其他方法获取testHashMap 泛型参数,
* 比如:Type[] genericArguments= ((ParameterizedType) testHashMap.getClass().getGenericSuperclass()).getActualTypeArguments()
* 此处局部变量testHashMap 写成HashMap 的匿名内部类(带 {} 声明)对象也是有特殊考虑的
* 使用者可自行思考其中缘由)
*
* @return: java.util.HashMap<String, com.zhzz.commom.utils.dto.CheckNullInfo>
* 为空的变量名称,检查时会层层递归,在上层变量名称基础上添加变量声明链路。集合,数组 Map 需要在检查后返回null元素的下标位置。
* 比如 variableCheckTestClass.data.resources(6).laneEles(4).crossCode
* public String variableName;
*
* 变量所属的类名称
* public String declareClass="";
*
* // 变量本身类型,如果是类型变量T ,通配泛型? extend T 或 ? super T 需要在对象状态为空时推断出实际类型。(null 情况无法实际使用对象的getClass 方法获取)
* public String variableType="";
*
* // 定义变量的上级对象的地址
* public String ownerObjectAddr="";
*
* @auther: wenyiking
* @date: 2021/1/20 21:04
*/
ReflectUtil reflectUtil = new ReflectUtil();
if (ReflectUtil.isNull(selfType, declaringType))
throw new Exception("需要检查的对象 Type 或者声明该变量的上层类 Type 信息有空值,无法继续判断");
HashMap<String, CheckNullInfo> checkNullInfoMap = new HashMap<>(4);
// 如果是空,则直接封装检查信息并返回
if (ReflectUtil.isNull(checkObject)) {
CheckNullInfo checkNullInfo = checkInfoEncapsulation(selfType.getTypeName(), declaringType.getTypeName(), variableName, ownerObject);
checkNullInfoMap.put(checkNullInfo.getVariableName(), checkNullInfo);
return checkNullInfoMap;
}
/*
获取迭代元素类型对应的处理策略,
对应类型: Class GenericArrayType ParameterizedType WildcardType TypeVariable 会有对应处理策略。
selfType.getClass().getName() 作为key
selfType 是 WildcardType(示例 ? entend T) 类型则key selfType.getClass().getName()="sun.reflect.generics.reflectiveObjects.WildcardTypeImpl"
selfType 是 ParameterizedType(示例 Set<String>) 类型则key selfType.getClass().getName()="sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl"
selfType 是 Class 类型则key selfType.getClass().getName()="java.lang.Class"
selfType 是 TypeVariable(示例 T) 类型则key selfType.getClass().getName()="sun.reflect.generics.reflectiveObjects.TypeVariableImpl"
selfType 是 GenericArrayType(示例 T[][]) 类型则key selfType.getClass().getName()="sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl"
*/
// genericStrategyHashMap 是在类初始化时就已经构建好,请查看静态字段
GenericStrategy genericStrategy = VariableRecurisonCheck.genericStrategyHashMap.get(selfType.getClass().getName());
HashMap<String, CheckNullInfo> recursionCheck = genericStrategy.recursionType(checkObject,
selfType,
declaringType,
variableName,
ownerObject,
genericArguments,
this);
//下层递归结果加入上层调用栈变量中
//深拷贝,避免内层调用时创建的List 对象中的元素被上层引用而无法释放,占用内存
deepCopyWithSerialize(recursionCheck, checkNullInfoMap);
return checkNullInfoMap;
}
public TypeVariable[] getTypeVariable(Class<?> resolveClass) throws Exception {
if (Objects.isNull(resolveClass))
throw new Exception("findTypeVariable(Class<?> resolveClass) 方法参数为空");
return resolveClass.getTypeParameters();
}
public int findSpecificTypeVariablePosition(String typeVariableName, Class<?> resolveClass) throws Exception {
if (ReflectUtil.isNull(typeVariableName))
throw new Exception("findSpecificTypeVariable 方法参数typeVariableName 为null ");
TypeVariable specificTypeVariable = null;
TypeVariable[] typeVariables = getTypeVariable(resolveClass);
int position = -1;
int iter = 0;
for (TypeVariable typeVariable : typeVariables) {
if (typeVariable.getName().contains(typeVariableName)) {
position = iter;
}
iter++;
}
return position;
}
public int findSpecificTypeVariablePosition(String typeVariableName, TypeVariable[] typeVariables) throws Exception {
/**
* @description: 根据声明的泛型类型的名称,比如 T ,找到其在申明泛型的类中是第几个泛型参数
* 比如 MyClass<S,K> 递归时发现是 S 类型的属性,S声明在第一个,则实参数组中也找对应的元素值作为S的实际类型。
* @param typeVariableName 泛型变量名称可通过方法获取,具体看策略实现类中的代码
* @param typeVariables 类型参数数组 比如 MyClass<S,K> 可通过先获取 GenericDeclaration类型的子类对象,然后 getTypeParameters()获取
* @return: int
* @auther: wenyiking
* @date: 2021/2/7 11:57
*/
if (ReflectUtil.isNull(typeVariableName))
throw new Exception("findSpecificTypeVariable 方法参数typeVariableName 为null ");
TypeVariable specificTypeVariable = null;
int position = -1;
int iter = 0;
for (TypeVariable typeVariable : typeVariables) {
if (typeVariable.getName().contains(typeVariableName)) {
position = iter;
}
iter++;
}
return position;
}
public <K, V> void deepCopyWithSerialize(HashMap<K, V> origianls, HashMap<K, V> copyDestination) {
/**
* @description: 深拷贝,通过序列化方式
* @param origianls
* @param copyDestination
* @return: void
* @auther: wenyiking
* @date: 2021/2/7 12:21
*/
if (Objects.isNull(origianls) || origianls.isEmpty()) {
// 空列表直接返回
return;
}
ObjectOutputStream objectOutputStream = null;
ObjectInputStream objectInputStream = null;
Set<Map.Entry<K, V>> entrySet = origianls.entrySet();
for (Map.Entry<K, V> ele : entrySet) {
try {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
objectOutputStream = new ObjectOutputStream(bout);
objectOutputStream.writeObject(ele.getValue());
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
objectInputStream = new ObjectInputStream(bin);
V copyObject = (V) objectInputStream.readObject();
copyDestination.put(ele.getKey(), copyObject);
if (!Objects.isNull(objectOutputStream)) {
objectInputStream.close();
} else if (!Objects.isNull(objectInputStream)) {
objectInputStream.close();
}
} catch (Exception e) {
LOG.error(e.toString());
}
}
}
public <T> void deepCopyWithSerialize(List<T> origianls, List<T> copyDestination) {
if (Objects.isNull(origianls) || origianls.isEmpty()) {
// 空列表直接返回
return;
}
ObjectOutputStream objectOutputStream = null;
ObjectInputStream objectInputStream = null;
for (T ele : origianls) {
try {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
objectOutputStream = new ObjectOutputStream(bout);
objectOutputStream.writeObject(ele);
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
objectInputStream = new ObjectInputStream(bin);
T copyObject = (T) objectInputStream.readObject();
copyDestination.add(copyObject);
if (!Objects.isNull(objectOutputStream)) {
objectInputStream.close();
} else if (!Objects.isNull(objectInputStream)) {
objectInputStream.close();
}
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
protected CheckNullInfo checkInfoEncapsulation(String selfTypeName, String declaringTypeName, String variableName, Object ownerObject) {
CheckNullInfo checkNullInfo = new CheckNullInfo();
checkNullInfo.setVariableType(selfTypeName);
checkNullInfo.setDeclareClass(declaringTypeName);
checkNullInfo.setVariableName(variableName);
checkNullInfo.setOwnerObjectAddr(ownerObject.toString());
return checkNullInfo;
}
public boolean isComponentType(Class<?> fieldType) {
/**
* @description: 判断是否是数组类型 比如 String[] 类型名称是[Ljava.lang.String,int[] [I
* @param fieldType
* @return: boolean
* @auther: wenyiking
* @date: 2021/1/18 15:36
*/
boolean flag = true;
flag = fieldType.getName().contains("[");
return false;
}
public Type getActualBound(WildcardType wildcardType) {
Type[] typeLowerBounds = wildcardType.getLowerBounds();
Type[] typeUpperBounds = wildcardType.getUpperBounds();
Type bound0 = null;
if (wildcardType.getTypeName().contains("super")) {
if (!ReflectUtil.isNull(typeLowerBounds) && typeLowerBounds.length != 0) {
bound0 = typeLowerBounds[0];
}
} else {
if (!ReflectUtil.isNull(typeUpperBounds) && typeUpperBounds.length != 0) {
bound0 = typeUpperBounds[0];
}
}
return bound0;
}
public boolean isBaseType(Class<?> fieldType) {
/**
* @description: 基础类型的包装类或者String 类型
* @param fieldType
* @return: boolean
* @auther: wenyiking
* @date: 2021/1/17 14:56
*/
return fieldType.isAssignableFrom(Integer.class) || fieldType.isAssignableFrom(Long.class)
|| fieldType.isAssignableFrom(Double.class) || fieldType.isAssignableFrom(String.class)
|| fieldType.isAssignableFrom(Float.class) || fieldType.isAssignableFrom(Boolean.class)|| fieldType.isAssignableFrom(Object.class);
//|| fieldType.isAssignableFrom(Object.class)
}
}
ReflectUtil 反射缓存类方法列表如下:
ReflectUtil 代码:
package com.shzz.commom.utils.reflect;
import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
import com.googlecode.concurrentlinkedhashmap.EvictionListener;
import com.googlecode.concurrentlinkedhashmap.Weighers;
import com.shzz.commom.utils.cache.EvictionListenerImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* @Classname ReflectUtil
* @Description TODO
* @Date 2021/1/15 17:17
* @Created by wenyiking
*/
public class ReflectUtil {
private final static Logger LOG = LoggerFactory.getLogger(ReflectUtil.class);
static {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("accessDeclaredMembers"));
}
}
//默认 ConcurrentLinkedHashMap 最大缓存数量,key 的个数,即5000个类的字段、方法、构造器数据
//
private static final int CAPACITY = 5000;
//监听数据剔除信息
private static EvictionListener<String, ConcurrentHashMap<String, Method>> methodEvictionListener = new EvictionListenerImpl<>("methodCache");
private static EvictionListener<String, ConcurrentHashMap<String, Field>> fieldEvictionListener = new EvictionListenerImpl<>("fieldCache");
private static EvictionListener<String, ConcurrentHashMap<String, Constructor<?>>> constructEvictionListener = new EvictionListenerImpl<>("constructCache");
// 缓存类的方法,字段,构造器,key 是Class 全限定名
//采用谷歌开源 concurrentlinkedhashmap-lru map 数据量达到阈值后可基于lru算法实现历史数据删除
private static volatile ConcurrentLinkedHashMap<String, ConcurrentHashMap<String, Method>> methodCache = new ConcurrentLinkedHashMap.Builder<String, ConcurrentHashMap<String, Method>>()
.maximumWeightedCapacity(CAPACITY).listener(methodEvictionListener).weigher(Weighers.singleton()).build();
private static volatile ConcurrentLinkedHashMap<String, ConcurrentHashMap<String, Field>> fieldCache = new ConcurrentLinkedHashMap.Builder<String, ConcurrentHashMap<String, Field>>()
.maximumWeightedCapacity(CAPACITY).listener(fieldEvictionListener).weigher(Weighers.singleton()).build();
private static volatile ConcurrentLinkedHashMap<String, ConcurrentHashMap<String, Constructor<?>>> constructCache = new ConcurrentLinkedHashMap.Builder<String, ConcurrentHashMap<String, Constructor<?>>>()
.maximumWeightedCapacity(CAPACITY).listener(constructEvictionListener).weigher(Weighers.singleton()).build();
public <T> ConcurrentHashMap<String, Method> getMethods(Class<T> classType) throws Exception {
ConcurrentHashMap<String, Method> methodConcurrentHashMap = new ConcurrentHashMap();
if (isNull(classType)) {
throw new Exception("传入的Class 类型参数为空");
} else {
if (methodCache.containsKey(classType.getName())) {
return methodCache.get(classType.getName());
} else {
// 所有方法,包含父类继承方法
Method[] methods = classType.getMethods();
for (Method method : methods) {
methodConcurrentHashMap.put(getMethodSignature(method), method);
}
methodCache.put(classType.getName(), methodConcurrentHashMap);
}
}
return methodConcurrentHashMap;
}
public String getMethodSignature(Method method) {
/**
* @description: 获取方法签名,返回值#方法名:参数列表
* @param method
* @return: java.lang.String 示例 本方法方法签名为: java.lang.String#getSignature:java.lang.refelct.Method
* @auther: wenyiking
* @date: 2021/1/17 11:45
*/
StringBuilder sb = new StringBuilder();
Class<?> returnType = method.getReturnType();
if (returnType != null) {
sb.append(returnType.getName()).append('#');
}
sb.append(method.getName());
Class<?>[] parameters = method.getParameterTypes();
for (int i = 0; i < parameters.length; ++i) {
if (i == 0) {
sb.append(':');
} else {
sb.append(',');
}
sb.append(parameters[i].getName());
}
return sb.toString();
}
public String getConstructSignature(Constructor<?> constructor) {
/**
* @description: 获取构造器签名,void#构造器名:参数列表
* @param method
* @return: java.lang.String 示例 : java.lang.String#getSignature:java.lang.refelct.Method
* @auther: wenyiking
* @date: 2021/1/17 11:45
*/
StringBuilder sb = new StringBuilder();
sb.append("void").append('#');
sb.append(constructor.getName());
Class<?>[] parameters = constructor.getParameterTypes();
for (int i = 0; i < parameters.length; ++i) {
if (i == 0) {
sb.append(':');
} else {
sb.append(',');
}
sb.append(parameters[i].getName());
}
return sb.toString();
}
public <T> ConcurrentHashMap<String, Field> getFields(Class<T> classType) throws Exception {
/**
* @description: 返回类的字段并缓存, 缓存会基于lru 删除
* @param classType
* @return: java.util.concurrent.ConcurrentHashMap<java.lang.String, java.lang.reflect.Field>
* @auther: wenyiking
* @date: 2021/1/22 18:19
*/
ConcurrentHashMap<String, Field> fieldConcurrentHashMap = new ConcurrentHashMap<>(16);
if (isNull(classType)) {
throw new Exception("传入的Class 类型参数为空");
} else {
if (fieldCache.containsKey(classType.getName())) {
return fieldCache.get(classType.getName());
} else {
//classType.getDeclaredFields()
Field[] fields = classType.getDeclaredFields();
for (Field field : fields) {
fieldConcurrentHashMap.put(field.getName(), field);
}
fieldCache.put(classType.getName(), fieldConcurrentHashMap);
}
}
return fieldConcurrentHashMap;
}
public <T> ConcurrentHashMap<String, Constructor<?>> getConstructs(Class<T> classType) throws Exception {
/**
* @description: 返回类的字段并缓存, 缓存会基于lru 删除
* @param classType
* @return: java.util.concurrent.ConcurrentHashMap<java.lang.String, java.lang.reflect.Field>
* @auther: wenyiking
* @date: 2021/1/22 18:19
*/
ConcurrentHashMap<String, Constructor<?>> constructConcurrentHashMap = new ConcurrentHashMap<>(16);
if (isNull(classType)) {
throw new Exception("传入的Class 类型参数为空");
} else {
if (constructCache.containsKey(classType.getName())) {
return constructCache.get(classType.getName());
} else {
//classType.getDeclaredFields()
Constructor[] constructors = classType.getDeclaredConstructors();
for (Constructor constructor : constructors) {
constructConcurrentHashMap.put(getConstructSignature(constructor), constructor);
}
constructCache.put(classType.getName(), constructConcurrentHashMap);
}
}
return constructConcurrentHashMap;
}
public static boolean isNull(Object... checkObjects) {
boolean flag = true;
for (Object o : checkObjects) {
flag = Objects.isNull(o);
}
return flag;
}
}
反射工具类中有个缓存的工具类,采用谷歌开源 concurrentlinkedhashmap-lru map 数据量达到阈值后可基于lru算法实现历史数据删除
自定义实现其监听接口,打印删除的类型反射信息
package com.shzz.commom.utils.cache;
import com.googlecode.concurrentlinkedhashmap.EvictionListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @Classname EvictionListenerImpl
* @Description TODO
* @Date 2021/2/1 12:47
* @Created by wenyiking
*/
public class EvictionListenerImpl<K,V> implements EvictionListener<K,V> {
private final static Logger LOG= LoggerFactory.getLogger(EvictionListenerImpl.class);
private String cacheVariableName;// 缓存名称
public EvictionListenerImpl(String cacheVariableName){
this.cacheVariableName=cacheVariableName;
}
@Override
public void onEviction(K k, V v) {
LOG.warn( cacheVariableName+" Evicted key=" + k);
}
}
GenericStrategy 是不同Type 类型的处理策略接口,在检查空值状态时推断其实际类型。有五种实现类
package com.shzz.commom.utils.reflect;
import com.shzz.commom.utils.dto.CheckNullInfo;
import java.lang.reflect.Type;
import java.util.HashMap;
/**
* @Classname GenericStrategy
* @Description TODO
* @Date 2021/1/24 10:19
* @Created by wenyiking
*/
public interface GenericStrategy {
HashMap<String, CheckNullInfo> recursionType(Object checkObject,
Type selfType,
Type declaringType,
String variableName,
Object ownerObject,
Type[] genericArguments, VariableRecurisonCheck variableRecurisonCheck) throws Exception;
}
- 实现类1 Class类型的处理策略
package com.shzz.commom.utils.reflect;
import com.shzz.commom.utils.annotation.CheckNullTag;
import com.shzz.commom.utils.dto.CheckNullInfo;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* @Classname ClassTypeProcess
* @Description TODO
* @Date 2021/1/19 10:38
* @Created by wenyiking
*/
final class ClassTypeProcess implements GenericStrategy {
private final static Logger LOG= LoggerFactory.getLogger(ClassTypeProcess.class);
@Override
public HashMap<String, CheckNullInfo> recursionType(Object checkObject,
Type selfType,
Type declaringClass,
String variableName,
Object ownerObject,
Type[] genericArguments,
VariableRecurisonCheck variableRecurisonCheck) throws Exception {
/**
* @description: 该方法是Class类型的处理策略,递归是如果是该类类型,上层调用方法会获取到该策略的缓存进行处理。
* 参数说明与VariableRecurisonCheck 类似
* @param checkObject 参见 VariableRecurisonCheck check 对应参数说明
* @param selfType 参见 VariableRecurisonCheck check 对应参数说明
* @param declaringClass 参见 VariableRecurisonCheck check 对应参数说明
* @param variableName 参见 VariableRecurisonCheck check 对应参数说明
* @param ownerObject 参见 VariableRecurisonCheck check 对应参数说明
* @param genericArguments 参见 VariableRecurisonCheck check 对应参数说明
* @param variableRecurisonCheck 上层调用方法将 variableRecurisonCheck 工具类对象参数,会通过该引用调用其中的部分方法。
* @return: java.util.List<com.zhzz.commom.utils.dto.CheckNullInfo> 参见 VariableRecurisonCheck check 对应参数说明
* 处理完 Class 类型对象的信息后会返回本次递归的结果。给到上层
* @auther: wenyiking
* @date: 2021/01/16 21:02
*/
if(!(selfType instanceof Class))
throw new Exception("selfType非Class类,实际类型是 " + selfType.getClass().getName() + " 非ClassTypeProcess 策略处理范围");
ReflectUtil reflectUtil = new ReflectUtil();
Class<?> selfClass=(Class)selfType;
HashMap<String,CheckNullInfo> checkNullInfoMap = new HashMap<>(4);
// 如果是空,则直接封装检查信息并返回
if (ReflectUtil.isNull(checkObject)) {
CheckNullInfo checkNullInfo = variableRecurisonCheck.checkInfoEncapsulation(selfClass.getName(), declaringClass.getTypeName(), variableName, ownerObject);
checkNullInfoMap.put(checkNullInfo.getVariableName(),checkNullInfo);
return checkNullInfoMap;
}
if (variableRecurisonCheck.isBaseType(selfClass)) {
// 嵌套类型最内层字段或者变量值的检查如果是基础类型则无需无需进一步递归解析基础类型内部信息。直接返回结果。
// 如果基础类型对象为空,则在上一步判空操作中会直接封装检查信息并返回。
return checkNullInfoMap;
}else if (selfClass.isArray()) {
//循环遍历数组
Class<?> componentType = selfClass.getComponentType();
int length = Array.getLength(checkObject);
for (int len = 0; len < length; ++len) {
Object arrayElem = Array.get(checkObject, len);
String arrayElemName = variableName + "[" + len + "]";
if (ReflectUtil.isNull(arrayElem)) {
CheckNullInfo checkNullInfo = variableRecurisonCheck.checkInfoEncapsulation(selfClass.getComponentType().getTypeName(), checkObject.getClass().getName(), arrayElemName, ownerObject);
checkNullInfoMap.put(checkNullInfo.getVariableName(),checkNullInfo);
continue;
}
//递归调用检查数组元素
HashMap<String,CheckNullInfo> recursionArrayComponent =variableRecurisonCheck.check(arrayElem, arrayElem.getClass(), selfClass, arrayElemName, checkObject,genericArguments);
//下层递归结果加入上层调用栈变量中
//深拷贝,避免内层调用时创建的List 对象中的元素被上层引用而无法释放,占用内存
variableRecurisonCheck.deepCopyWithSerialize(recursionArrayComponent, checkNullInfoMap);
}
}else{
// 除了基本类型、集合类型、Map(这些是参数化类型)等之外的类型,包括用户自定义实体类或者其他类
ConcurrentHashMap<String, Field> fieldMap = reflectUtil.getFields(selfClass);
Set<Map.Entry<String, Field>> fieldEntry = fieldMap.entrySet();
for (Map.Entry<String, Field> entry : fieldEntry) {
Field field = entry.getValue();
Class<?> fieldType = field.getType();
field.setAccessible(true);
//上层元素变量名称信息拼接,便于直观查看变量的声明链路
String fullyName = variableName + "." + field.getName();
//System.out.println(fullyName);
// 深度优先递归检查该字段控制情况
Object fieldValue = field.get(checkObject);
CheckNullTag checkNullTag=field.getAnnotation(CheckNullTag.class);
// 有CheckNullTag 标签且 check 检查项为false
if(!Objects.isNull(checkNullTag)&&(!checkNullTag.check())){
LOG.info(checkNullTag.description());
continue;
}
//根据泛型类型自动选择处理策略。
Type fieldGenericType = field.getGenericType();
GenericStrategy genericStrategy=VariableRecurisonCheck.genericStrategyHashMap.get(fieldGenericType.getClass().getName());
HashMap<String,CheckNullInfo> recursionField= genericStrategy.recursionType(fieldValue,fieldGenericType,checkObject.getClass(),
fullyName,checkObject,genericArguments,variableRecurisonCheck);
//下层递归结果加入上层调用栈变量中
//深拷贝,避免内层调用时创建的List 对象中的元素被上层引用而无法释放,占用内存
variableRecurisonCheck.deepCopyWithSerialize(recursionField, checkNullInfoMap);
}
}
return checkNullInfoMap;
}
}
- 实现类2 GenericArrayType类型(T[][] 或者其他参数化泛型数组)的处理策略
package com.shzz.commom.utils.reflect;
import com.shzz.commom.utils.dto.CheckNullInfo;
import java.lang.reflect.*;
import java.util.HashMap;
/**
* @Classname GenericArrayTypeProcess
* @Description TODO
* @Date 2021/1/19 17:46
* @Created by wenyiking
*/
public class GenericArrayTypeProcess implements GenericStrategy {
@Override
public HashMap<String, CheckNullInfo> recursionType(Object checkObject, Type selfType, Type declaringType, String variableName, Object ownerObject, Type[] genericArguments, VariableRecurisonCheck variableRecurisonCheck) throws Exception {
/**
* @description: 该方法是GenericArrayType类型(T[][])的处理策略,递归是如果是该类类型,上层调用方法会获取到该策略的缓存进行处理。
* 参数说明与VariableRecurisonCheck 类似
* @param checkObject 参见 VariableRecurisonCheck check 对应参数说明
* @param selfType 参见 VariableRecurisonCheck check 对应参数说明
* @param declaringClass 参见 VariableRecurisonCheck check 对应参数说明
* @param variableName 参见 VariableRecurisonCheck check 对应参数说明
* @param ownerObject 参见 VariableRecurisonCheck check 对应参数说明
* @param genericArguments 参见 VariableRecurisonCheck check 对应参数说明
* @param variableRecurisonCheck 上层调用方法将 variableRecurisonCheck 工具类对象参数,会通过该引用调用其中的部分方法。
* @return: java.util.List<com.zhzz.commom.utils.dto.CheckNullInfo> 参见 VariableRecurisonCheck check 对应参数说明
* 处理完 GenericArrayType 类型对象的信息后会返回本次递归的结果。给到上层
* @return: java.util.List<com.zhzz.commom.utils.dto.CheckNullInfo>
* @auther: wenyiking
* @date: 2021/1/16 21:03
*/
if (!(selfType instanceof GenericArrayType))
throw new Exception("selfType非GenericArrayType类,实际类型是 " + selfType.getClass().getName() + " 非GenericArrayTypeProcess 策略处理范围");
GenericArrayType genericArrayType = (GenericArrayType) selfType;
HashMap<String, CheckNullInfo> checkNullInfoMap = new HashMap<>(4);
String genericArrayTypeName = genericArrayType.getTypeName();
// 泛型数组实际类型名需要基于genericComponentType的 TypeName做适当修改,不然显示的泛型数组类型就如T[] T[][] 这种,实际上
//我们需要表示成实际类型的数组比如 [Ljava.lang.String
//arrayElem.toString 可以得到实际数组类型。为空时不可用。需要通过其他方式获取。
// String[] genericArraySplit= genericArrayTypeName.split("\\[");
//标记泛型数组的泛型变量在泛型申明类的泛型声明数组中的位置
int position = 0;
int dem = 1; //维数统计
Type genericComponentType = genericArrayType.getGenericComponentType(); //泛型数组的组件类型
Type temp = genericComponentType;
while ((temp instanceof GenericArrayType)) {
//循环判断,直到最内层泛型参数
temp = ((GenericArrayType) temp).getGenericComponentType();
dem = dem + 1;
}
String checkObjectActualTypeName = "";
for (int i = 0; i < dem; ++i) {
checkObjectActualTypeName += "[";
}
//转化为TypeVariable
if (temp instanceof TypeVariable) {
TypeVariable tempTypeVariable = (TypeVariable) temp;
//获取泛型声明类的所有类型变量信息,从中找出对应的实际参数
//1 .先获取temp 所对应的类型变量名称
String typeVariableName = tempTypeVariable.getTypeName();
//2. 从类型变量的声明类中找到temp 所对应的索引位置
TypeVariable<?>[] tempTypeParameters = tempTypeVariable.getGenericDeclaration().getTypeParameters();
position = variableRecurisonCheck.findSpecificTypeVariablePosition(typeVariableName, tempTypeParameters);
if (position < 0) {
throw new Exception("泛型实参位置查询错误");
}
checkObjectActualTypeName += genericArguments[position].getTypeName();//实际泛型对应的类型在上层传入参数中提取
} else if (temp instanceof ParameterizedType) {
ParameterizedType tempParameterizedType = (ParameterizedType) temp;
checkObjectActualTypeName += tempParameterizedType.getRawType();
} else {
throw new Exception("非泛型数组类,不予处理");
}
// 如果泛型数组是空,则直接封装检查信息并返回
if (ReflectUtil.isNull(checkObject)) {
CheckNullInfo checkNullInfo = variableRecurisonCheck.checkInfoEncapsulation(checkObjectActualTypeName, declaringType.getTypeName(), variableName, ownerObject);
checkNullInfoMap.put(checkNullInfo.getVariableName(), checkNullInfo);
return checkNullInfoMap;
}
int length = Array.getLength(checkObject);
for (int len = 0; len < length; ++len) {
Object arrayElem = Array.get(checkObject, len);
String arrayElemName = variableName + "[" + len + "]";
if (ReflectUtil.isNull(arrayElem)) {
genericComponentType.getClass().cast(arrayElem);
String elemActualTypeName = "";
for (int i = 0; i < dem - 1; ++i) {
//内层类型维数上限在原泛型数组维数基础上-1.
// 比如 T[][] dem=2
// 循环内层是T[]
elemActualTypeName += "[";
}
if (temp instanceof TypeVariable) {
elemActualTypeName += (genericArguments[position].getTypeName());//实际泛型对应的类型在上层传入参数中提取
} else if (temp instanceof ParameterizedType) {
ParameterizedType tempParameterizedType = (ParameterizedType) temp;
elemActualTypeName += tempParameterizedType.getTypeName();//实际泛型对应的类型在上层传入参数中提取
}
CheckNullInfo checkNullInfo = variableRecurisonCheck.checkInfoEncapsulation(elemActualTypeName, checkObjectActualTypeName, arrayElemName, ownerObject);
checkNullInfoMap.put(checkNullInfo.getVariableName(), checkNullInfo);
continue;
}
//递归调用检查数组元素
//List<CheckNullInfo> recursionArrayComponent =variableRecurisonCheck.check(arrayElem, genericComponentType, selfType, arrayElemName, checkObject,genericArguments);
GenericStrategy genericStrategy = VariableRecurisonCheck.genericStrategyHashMap.get(genericComponentType.getClass().getName());
HashMap<String, CheckNullInfo> recursionArrayComponent = genericStrategy.recursionType(arrayElem, genericComponentType, selfType,
arrayElemName, checkObject, genericArguments, variableRecurisonCheck);
/*
下层递归结果加入上层调用栈变量中
深拷贝,避免内层调用时创建的List 对象中的元素被上层引用而无法释放,占用内存
*/
variableRecurisonCheck.deepCopyWithSerialize(recursionArrayComponent, checkNullInfoMap);
}
return checkNullInfoMap;
}
}
- ParameterizedType类型(Set 或嵌套泛型类Set T由上层声明)的处理策略
package com.shzz.commom.utils.reflect;
import com.shzz.commom.utils.dto.CheckNullInfo;
import java.lang.reflect.*;
import java.util.*;
/**
* @Classname ParameterizedTypeProcess
* @Description TODO
* @Date 2021/1/19 10:37
* @Created by wenyiking
*/
final class ParameterizedTypeProcess implements GenericStrategy {
@Override
public HashMap<String, CheckNullInfo> recursionType(Object checkObject,
Type selfType,
Type declaringType,
String variableName,
Object ownerObject,
Type[] genericArguments,
VariableRecurisonCheck variableRecurisonCheck) throws Exception {
/**
* @description: 该方法是ParameterizedType类型(Set<String> 或嵌套泛型类Set<T> T由上层声明)的处理策略,递归是如果是该类类型,上层调用方法会获取到该策略的缓存进行处理。
* 参数说明与VariableRecurisonCheck 类似
* @param checkObject 参见 VariableRecurisonCheck check 对应参数说明
* @param selfType 参见 VariableRecurisonCheck check 对应参数说明
* @param declaringClass 参见 VariableRecurisonCheck check 对应参数说明
* @param variableName 参见 VariableRecurisonCheck check 对应参数说明
* @param ownerObject 参见 VariableRecurisonCheck check 对应参数说明
* @param genericArguments 参见 VariableRecurisonCheck check 对应参数说明
* @param variableRecurisonCheck 上层调用方法将 variableRecurisonCheck 工具类对象参数,会通过该引用调用其中的部分方法。
* @return: java.util.List<com.zhzz.commom.utils.dto.CheckNullInfo> 参见 VariableRecurisonCheck check 对应参数说明
* 处理完 ParameterizedType 类型对象的信息后会返回本次递归的结果。给到上层
* @return: java.util.List<com.zhzz.commom.utils.dto.CheckNullInfo>
* @auther: wenyiking
* @date: 2021/1/16 21:03
*/
if (!(selfType instanceof ParameterizedType))
throw new Exception("selfType非ParameterizedType类,实际类型是 " + selfType.getClass().getName() + " 非ParameterizedTypeProcess策略处理范围");
HashMap<String,CheckNullInfo> checkNullInfoMap = new HashMap<>(4);
ParameterizedType parameterizedType = ((ParameterizedType) selfType);
//泛型类的原始类型,比如 List<String> rawType java.util.List
Class<?> rawType = (Class<?>) parameterizedType.getRawType();
// 如果泛型类型是空,则直接封装检查信息并返回
if (ReflectUtil.isNull(checkObject)) {
CheckNullInfo checkNullInfo = variableRecurisonCheck.checkInfoEncapsulation(rawType.getName(), declaringType.getTypeName(), variableName, ownerObject);
checkNullInfoMap.put(checkNullInfo.getVariableName(),checkNullInfo);
return checkNullInfoMap;
}
Type elemType = null;
//解析该对象变量的声明类的实际参数数组,针对数组中元素的类型进行判别,绑定实际类型,主要针对WildcardType和TypeVariable 两种进行修改绑定。
Type[] genericArgumentsNew=parameterizedType.getActualTypeArguments();
int typeVariableReplacePosition=0;
int wildcardTypeUpperBound0ReplacePosition=0;
for(Type genericArgument:genericArgumentsNew ){
// System.out.println("遍历 genericArgumentsNew["+typeVariableReplacePosition+"].getTypeName="+genericArgument.getTypeName());
if(genericArgument instanceof TypeVariable){
//如果本层的某个泛型实参与上层泛型实参关联,为了得到准确类型,需要从上层传入的实参中匹配提取并替换本层泛型实参中的对应元素
TypeVariable<?>[] genericDeclarationTypeParameters= ((TypeVariable)genericArgument).getGenericDeclaration().getTypeParameters();
int typeVariableIndex= variableRecurisonCheck.findSpecificTypeVariablePosition(genericArgument.getTypeName(),genericDeclarationTypeParameters);
if(typeVariableIndex<0){
throw new Exception("泛型类型下标值异常("+typeVariableIndex+"),非正常值");
}
genericArgumentsNew[typeVariableReplacePosition]=genericArguments[typeVariableIndex];
}else if(genericArgument instanceof WildcardType){
WildcardType wildcardType = (WildcardType) genericArgument;
// 获取实际泛型上下界类型
Type bound0=variableRecurisonCheck.getActualBound(wildcardType);
//如果本层的某个通配泛型实参与上层泛型实参关联,,比如 ?extends T T 是上层定义泛型变量,为了得到准确类型,需要从上层传入的实参中匹配提取并替换本层泛型实参中的对应元素
if(bound0 instanceof TypeVariable){
TypeVariable wildcardTypeBound0=((TypeVariable)bound0);
TypeVariable<?>[] genericDeclarationWildcardTypeBound= wildcardTypeBound0.getGenericDeclaration().getTypeParameters();
int wildcardTypeUpperBound0Index= variableRecurisonCheck.findSpecificTypeVariablePosition(bound0.getTypeName(),genericDeclarationWildcardTypeBound);
genericArgumentsNew[wildcardTypeUpperBound0ReplacePosition]=genericArguments[wildcardTypeUpperBound0Index];
}else{
genericArgumentsNew[wildcardTypeUpperBound0ReplacePosition]=bound0;
}
}
++typeVariableReplacePosition;
++wildcardTypeUpperBound0ReplacePosition;
}
//根据不同情况选择具体类型的处理策略
GenericStrategy genericStrategy=null;
//参数化类型有多种形式,本函数聚焦于两种情况
//1. Map 或者集合类及其子类,这种类型或继续递归遍历 内部元素并判断元素空状态
//2. MyClass<T> 自定义的泛型类,这种类型无需继续递归泛型实际类型String 只需要判断MyClass 对象或者其字段的空状态
//元素不为空,且是集合或者Map 这种参数化类型时,继续深度优先遍历内层数据
if (Collection.class.isAssignableFrom(rawType)) {
//集合类型
// 先转为集合类处理
elemType = genericArgumentsNew[0];
if(Objects.isNull(elemType)){
throw new Exception("集合中元素的类型推断 elemType 为空");
}
Collection collection = (Collection) checkObject;
Iterator<?> collectionIterator = collection.iterator();
int elemPosition = 0;
while (collectionIterator.hasNext()) {
//将本层次迭代信息加入变量名中
String iteratorElementName = variableName + "(" + elemPosition + ")";
Object iteratorElement = collectionIterator.next();
if (ReflectUtil.isNull(iteratorElement)) {
//为空直接返回结果
CheckNullInfo checkNullInfo = variableRecurisonCheck.checkInfoEncapsulation(elemType.getTypeName(), rawType.getTypeName(), iteratorElementName, checkObject);
checkNullInfoMap.put(checkNullInfo.getVariableName(),checkNullInfo);
continue;
}
// 获取Collection迭代元素类型对应的处理策略,,key 为elemType.getClass().getName()
// genericStrategy = VariableRecurisonCheck.genericStrategyHashMap.get(elemType.getClass().getName());
if(elemType instanceof Class){
if(elemType instanceof Class){
/* 如果推断是Class 类型,则需要通过元素的getClass()获取实际类型。
这种情况elemType为何不可用上面类型推断的结果,需要基于实际元素重新修改类型?因为泛型参数化或者类型参数推断的类型可能对应的实际数据是其子类实现类
比如 List<Object>,Object ,可以接受任意子类,处理策略是ClassTypeProcess 不过 反射分析Object字段时并无实际意义,故不做处理
如果推断是非 Class 类型,那么通过元素的getClass()获取类型只会是Class 类型, 就会失去原始意义。
List<? super ArrayList<? super Integer>> listList 第一层迭代时得到的还是List 子类
通过 iteratorElement.getClass()获取的是Class 类型 ,
那么后面选出该元素的处理策时会以ClassTypeProcess来处理,内部会反射集合类的字段元素。
但其实内部很多字段与我们分析的业务类数据不相关,只希望迭代处理这些类型的元素来分析
这种情况需要保留原来类型推断的结果 sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
*/
elemType=iteratorElement.getClass();
}
genericStrategy = VariableRecurisonCheck.genericStrategyHashMap.get(elemType.getClass().getName());
HashMap<String,CheckNullInfo> recursionCollectionParameterizedType=genericStrategy.recursionType(iteratorElement,
elemType,
selfType,
iteratorElementName,
checkObject,
genericArguments,
variableRecurisonCheck);
//下层递归结果加入上层调用栈变量中
//深拷贝,避免内层调用时创建的List 对象中的元素被上层引用而无法释放,占用内存
variableRecurisonCheck.deepCopyWithSerialize(recursionCollectionParameterizedType, checkNullInfoMap);
// 元素指针位置移动
elemPosition++;
}
} else if (Map.class.isAssignableFrom(rawType)) {
int elemPosition = 0;
Map map = (Map) checkObject;
Set<?> keySet = map.keySet();
// value 是Map类型变量
//这里需要从上面提取得到的新泛型实参数组中获取元素类型。
elemType = genericArgumentsNew[1];
if(Objects.isNull(elemType)){
throw new Exception("Map中元素的类型推断 elemType 为空");
}
for (Object key : keySet) {
//
Object mapValue = map.get(key);
String upperKeyValue = "";
String elemNameDescription = variableName + "(" + key.toString() + ")";
// Map 泛型参数中Value 值的对应的所以是1.比如 Map<String,Map<String,String>> ,key 对应的类型声明是0 Value 对应的类型声明的索引就是1,
if (ReflectUtil.isNull(mapValue)) {
CheckNullInfo checkNullInfo = variableRecurisonCheck.checkInfoEncapsulation(elemType.getTypeName(), rawType.getTypeName(), elemNameDescription, checkObject);
checkNullInfoMap.put(checkNullInfo.getVariableName(),checkNullInfo);
continue;
}
// 获取Map迭代元素类型对应的处理策略,key 为elemType.getClass().getName()
// genericStrategy = VariableRecurisonCheck.genericStrategyHashMap.get(elemType.getClass().getName());
if(elemType instanceof Class){
/*
如果推断是Class 类型,则需要通过元素的getClass()获取实际类型。
这种情况elemType为何不可用上面类型推断的结果,需要基于实际元素重新修改类型?因为泛型参数化或者类型参数推断的类型可能对应的实际数据是其子类实现类
比如 Map<String,Object> 参数化类型推断只能是Object,实际加入类型可以使 任意子类,对应处理策略 ClassTypeProcess
不过 反射分析Object字段时并无实际意义,故不做处理
如果推断是非 Class 类型,那么通过元素的getClass()获取类型,获取的是Class 类型。就会失去原始意义。
Map<String, ? super HashMap<String,? super Integer>> mapMap 第一层迭代时得到的还是Map 子类
通过 mapValue.getClass()获取的是Class 类型 ,
那么后面选出该元素的处理策时会以ClassTypeProcess来处理,内部会反射Map 字段元素。
但其实内部很多字段与我们分析的业务类数据不相关,只希望迭代处理这些类型的元素来分析
这种情况需要保留原来类型推断的结果 sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
*/
elemType=mapValue.getClass();
}
// elemType=mapValue.getClass();
genericStrategy = VariableRecurisonCheck.genericStrategyHashMap.get(elemType.getClass().getName());
HashMap<String,CheckNullInfo> recursionMapParameterizedType=genericStrategy.recursionType(mapValue,
elemType,
selfType,
elemNameDescription,
checkObject,
genericArguments,
variableRecurisonCheck);
//下层递归结果加入上层调用栈变量中
//深拷贝,避免内层调用时创建的List 对象中的元素被上层引用而无法释放,占用内存
variableRecurisonCheck.deepCopyWithSerialize(recursionMapParameterizedType, checkNullInfoMap);
// 元素指针位置移动
elemPosition++;
}
}else{
//其他参数化类型,比如MyClass<Stirng> myclass MyClass<T> myclass T是在上层类定义的泛型
//常规泛型类处理策略,rawType.getClass().getName()
genericStrategy= VariableRecurisonCheck.genericStrategyHashMap.get(rawType.getClass().getName());
HashMap<String,CheckNullInfo> recursionNormalParameterizedType=genericStrategy.recursionType(checkObject,rawType,declaringType,variableName,ownerObject,genericArgumentsNew,variableRecurisonCheck);
//下层递归结果加入上层调用栈变量中
//深拷贝,避免内层调用时创建的List 对象中的元素被上层引用而无法释放,占用内存
variableRecurisonCheck.deepCopyWithSerialize(recursionNormalParameterizedType, checkNullInfoMap);
}
return checkNullInfoMap;
}
}
4.TypeVariable类型(T)的处理策略
package com.shzz.commom.utils.reflect;
import com.shzz.commom.utils.dto.CheckNullInfo;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
/**
* @Classname TypeVariableProcess
* @Description TODO
* @Date 2021/1/19 10:39
* @Created by wenyiking
*/
final class TypeVariableProcess implements GenericStrategy {
@Override
public HashMap<String, CheckNullInfo> recursionType(Object checkObject,
Type selfType,
Type declaringType,
String variableName,
Object ownerObject,
Type[] genericArguments,
VariableRecurisonCheck variableRecurisonCheck) throws Exception {
/**
* @description: 该方法是TypeVariable类型(T)的处理策略,递归是如果是该类类型,上层调用方法会获取到该策略的缓存进行处理。
* 参数说明与VariableRecurisonCheck 类似
* @param checkObject 参见 VariableRecurisonCheck check 对应参数说明
* @param selfType 参见 VariableRecurisonCheck check 对应参数说明
* @param declaringClass 参见 VariableRecurisonCheck check 对应参数说明
* @param variableName 参见 VariableRecurisonCheck check 对应参数说明
* @param ownerObject 参见 VariableRecurisonCheck check 对应参数说明
* @param genericArguments 参见 VariableRecurisonCheck check 对应参数说明
* @param variableRecurisonCheck 上层调用方法将 variableRecurisonCheck 工具类对象参数,会通过该引用调用其中的部分方法。
* @return: java.util.List<com.zhzz.commom.utils.dto.CheckNullInfo> 参见 VariableRecurisonCheck check 对应参数说明
* 处理完 TypeVariable 类型对象的信息后会返回本次递归的结果。给到上层
* @return: java.util.List<com.zhzz.commom.utils.dto.CheckNullInfo>
* @auther: wenyiking
* @date: 2021/1/16 21:03
*/
if (!(selfType instanceof TypeVariable))
throw new Exception("selfType非TypeVariable类,实际类型是 " + selfType.getClass().getName() + " 非TypeVariableProcess策略处理范围");
HashMap<String,CheckNullInfo> checkNullInfoMap = new HashMap<>(4);
TypeVariable selfTypeVariable=(TypeVariable)selfType;
//获取泛型声明类的所有类型变量信息,从中找出对应的实际参数
//1 .先获取selfType 所对应的类型变量名称
String typeVariableName=selfTypeVariable.getTypeName();
//2. 从类型变量的声明类中找到selfType 所对应的索引位置
TypeVariable<?>[] genericDeclarationTypeParameters=selfTypeVariable.getGenericDeclaration().getTypeParameters();
int selfTypeVariableIndex= variableRecurisonCheck.findSpecificTypeVariablePosition(typeVariableName,genericDeclarationTypeParameters);
//3. 从传入参数genericArguments 数组中找到对应的实际类型
Type actualSelfType=genericArguments[selfTypeVariableIndex];
// 如果泛型类型是空,则直接封装检查信息并返回
if (ReflectUtil.isNull(checkObject)) {
CheckNullInfo checkNullInfo = variableRecurisonCheck.checkInfoEncapsulation(actualSelfType.getTypeName(), declaringType.getTypeName(), variableName, ownerObject);
checkNullInfoMap.put(checkNullInfo.getVariableName(),checkNullInfo);
return checkNullInfoMap;
}
// 不为空,则根据绑定的实际类型信息继续递归判断。
// 获取迭代元素类型对应的处理策略
GenericStrategy genericStrategy = VariableRecurisonCheck.genericStrategyHashMap.get(actualSelfType.getClass().getName());
HashMap<String,CheckNullInfo> recursionTypeVariable=genericStrategy.recursionType(checkObject,actualSelfType,declaringType,variableName,ownerObject,genericArguments,variableRecurisonCheck);
//下层递归结果加入上层调用栈变量中
//深拷贝,避免内层调用时创建的List 对象中的元素被上层引用而无法释放,占用内存
variableRecurisonCheck.deepCopyWithSerialize(recursionTypeVariable, checkNullInfoMap);
return checkNullInfoMap;
}
}
5.WildcardType类型(? extend T)的处理策略
package com.shzz.commom.utils.reflect;
import com.shzz.commom.utils.dto.CheckNullInfo;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.HashMap;
/**
* @Classname WildcardTypeProcess
* @Description TODO
* @Date 2021/1/19 10:39
* @Created by wenyiking
*/
final class WildcardTypeProcess implements GenericStrategy {
@Override
public HashMap<String, CheckNullInfo> recursionType(Object checkObject,
Type selfType,
Type declaringType,
String variableName,
Object ownerObject,
Type[] genericArguments,
VariableRecurisonCheck variableRecurisonCheck) throws Exception {
/**
* @description: 该方法是WildcardType类型(? extend T)的处理策略,递归是如果是该类类型,上层调用方法会获取到该策略的缓存进行处理。
* 参数说明与VariableRecurisonCheck 类似
* @param checkObject 参见 VariableRecurisonCheck check 对应参数说明
* @param selfType 参见 VariableRecurisonCheck check 对应参数说明
* @param declaringClass 参见 VariableRecurisonCheck check 对应参数说明
* @param variableName 参见 VariableRecurisonCheck check 对应参数说明
* @param ownerObject 参见 VariableRecurisonCheck check 对应参数说明
* @param genericArguments 参见 VariableRecurisonCheck check 对应参数说明
* @param variableRecurisonCheck 上层调用方法将 variableRecurisonCheck 工具类对象参数,会通过该引用调用其中的部分方法。
* @return: java.util.List<com.zhzz.commom.utils.dto.CheckNullInfo> 参见 VariableRecurisonCheck check 对应参数说明
* 处理完 WildcardType 类型对象的信息后会返回本次递归的结果。给到上层
* @return: java.util.List<com.zhzz.commom.utils.dto.CheckNullInfo>
* @auther: wenyiking
* @date: 2021/1/16 21:03
*/
if (!(selfType instanceof WildcardType))
throw new Exception("selfType非WildcardType类,实际类型是 " + selfType.getClass().getName() + " 非WildcardType策略处理范围");
HashMap<String,CheckNullInfo> checkNullInfoMap = new HashMap<>(4);
//内层类是通配泛型类情况
//比如 Map<String, ? super HashMap<String,? super Integer>>
// ? super HashMap<String,? super Integer> 是通配参数类型,实际使用时 只用其边界类HashMap<String,? super Integer> 作为需要的需要的实参类型
WildcardType wildcardType = (WildcardType) selfType;
Type bound0=variableRecurisonCheck.getActualBound(wildcardType);
// 获取迭代元素类型对应的处理策略
GenericStrategy genericStrategy = VariableRecurisonCheck.genericStrategyHashMap.get(bound0.getClass().getName());
HashMap<String,CheckNullInfo> recursionWildcardType=genericStrategy.recursionType(checkObject,bound0,declaringType,variableName,ownerObject,genericArguments,variableRecurisonCheck);
//下层递归结果加入上层调用栈变量中
//深拷贝,避免内层调用时创建的List 对象中的元素被上层引用而无法释放,占用内存
variableRecurisonCheck.deepCopyWithSerialize(recursionWildcardType, checkNullInfoMap);
return checkNullInfoMap;
}
}
3 测试效果
常规实体类,不含嵌套结构的比较简单,读者可自行测试,本文以复杂定义的类对象来测试本功能。
测试实体类如下:
package test.entity;
import com.shzz.commom.utils.annotation.CheckNullTag;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class VariableCheckTestClass<T> implements Cloneable, Serializable {
List<? super MyClass<T,? extends T>> listMyClass=new ArrayList<>();
public T[][] tvalue;
public MyClass<T,? extends T> myClass;
public MyClass<T,T>[] myClassArray;
// @CheckNullTag(check = false)
public List<? extends T> listWild;
public List<? super ArrayList<? super ArrayList<? super T>>> nestList=new ArrayList<>();
// @CheckNullTag(check = false)
public String[] stringArray=null;
public ArrayList<ArrayList<String>> list=new ArrayList<ArrayList<String>>();
// @CheckNullTag(check = false)
public Map<String, ? super HashMap<String,? super Integer>> mapMap=new HashMap<String,HashMap<String,? super Integer>>();
{
ArrayList<String> innerList=new ArrayList<String>();
innerList.add("innerList element first");
innerList.add(null);
innerList.add("innerList element third");
list.add(innerList);
HashMap<String,? super Integer> map=new HashMap<>();
Integer integer1=new Integer(1);
map.put("secondlayer-first", integer1);
map.put("secondlayer-second",null);
map.put("secondlayer-third",new Integer(3));
mapMap.put("firstlayer",map);
ArrayList<? super ArrayList<? super T>> innerArrayList=new ArrayList<>();
ArrayList<? super T> sencondInner=new ArrayList<>();
sencondInner.add((T)("nestList-firstlayer-1"));
sencondInner.add(null);
sencondInner.add((T)("nestList-firstlayer-3"));
innerArrayList.add(sencondInner);
nestList.add(innerArrayList);
MyClass<T,? extends T> myClass=new MyClass<>();
myClass.setMyClassTfield((T)"listMyclass.TField");
myClass.setMyClassKfield(null);
listMyClass.add(myClass);
}
public T[][] getTvalue() {
return tvalue;
}
public void setTvalue(T[][] tvalue) {
this.tvalue = tvalue;
}
public MyClass<T, ? extends T> getMyClass() {
return myClass;
}
public void setMyClass(MyClass<T, ? extends T> myClass) {
this.myClass = myClass;
}
public List<? extends T> getListWild() {
return listWild;
}
public void setListWild(List<? extends T> listWild) {
this.listWild = listWild;
}
public String[] getStringArray() {
return stringArray;
}
public void setStringArray(String[] stringArray) {
this.stringArray = stringArray;
}
public ArrayList<ArrayList<String>> getList() {
return list;
}
public void setList(ArrayList<ArrayList<String>> list) {
this.list = list;
}
public Map<String, ? super HashMap<String, ? super Integer>> getMapMap() {
return mapMap;
}
public void setMapMap(Map<String, ? super HashMap<String, ? super Integer>> mapMap) {
this.mapMap = mapMap;
}
}
嵌套的自定义泛型类如下:
package test.entity;
import java.io.Serializable;
public class MyClass<S,K> implements Serializable,Cloneable {
S myClassTfield;
K myClassKfield;
public S getMyClassTfield() {
return myClassTfield;
}
public void setMyClassTfield(S myClassTfield) {
this.myClassTfield = myClassTfield;
}
public K getMyClassKfield() {
return myClassKfield;
}
public void setMyClassKfield(K myClassKfield) {
this.myClassKfield = myClassKfield;
}
}
测试方法:
package test;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.shzz.commom.utils.dto.CheckNullInfo;
import com.shzz.commom.utils.reflect.ReflectUtil;
import com.shzz.commom.utils.reflect.VariableRecurisonCheck;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import test.entity.MyClass;
import test.entity.TdeIacOriginalVehicle;
import test.entity.VariableCheckTestClass;
import java.lang.reflect.Type;
import java.util.*;
/**
* @Classname JunitTest
* @Description TODO
* @Date 2021/1/18 15:19
* @Created by wenyiking
*/
public class JunitTest {
private final static Logger LOG= LoggerFactory.getLogger(JunitTest.class);
@Test
public void test(){
VariableRecurisonCheck variableRecurisonCheck=VariableRecurisonCheck.getInstance();
VariableCheckTestClass<String> variableCheckTestClass =new VariableCheckTestClass();
//CheckNullInfo<CheckNullInfo> checkNullInfoVariable =new CheckNullInfo();
String[][] strings={{"11",null,"13"},{"21",null,"23"}};
variableCheckTestClass.tvalue=strings;
MyClass<String,? extends String> myClass=new MyClass<>();
myClass.setMyClassTfield(null);
variableCheckTestClass.myClass=myClass;
List<String> mylist=new ArrayList<>();
mylist.add(null);
MyClass<String,String>[] myClassArray=(MyClass<String,String>[])new MyClass[1];
MyClass<String,String> myClassArrayEle=new MyClass<>();
myClassArrayEle.setMyClassTfield("myClassArray.TField");
myClassArrayEle.setMyClassKfield(null);
myClassArray[0]=myClassArrayEle;
variableCheckTestClass.myClassArray=myClassArray;
variableCheckTestClass.listWild=mylist;
String[] stringArray={"stringArray",null,"last"};
variableCheckTestClass.setStringArray(stringArray);
HashMap<String,String> testHashMap=new HashMap<String,String>(){};
testHashMap.put("first","first");
testHashMap.put("second",null);
testHashMap.put("third","third");
try {
System.out.println("全量解析对象数据并检查null数据信息");
Type[] act={String.class};//外部泛型实参传入,有些可内部通过方法获取,但增加该参数可方便解析各类局部泛型变量的泛型实参。
HashMap<String,CheckNullInfo> checkNullInfoMap = variableRecurisonCheck.check(variableCheckTestClass, variableCheckTestClass.getClass(), JunitTest.class, "variableCheckTestClass",this,act);
Set<Map.Entry<String,CheckNullInfo>> entrySet=checkNullInfoMap.entrySet();
for(Map.Entry<String,CheckNullInfo> checkNullInfo : entrySet){
System.out.println(checkNullInfo.getValue().toString());
}
System.out.println();
System.out.println("基于全量解析的结构查询特定字段是否空值");
List<CheckNullInfo> checkNullInfoList= variableRecurisonCheck.checkSpecificField(checkNullInfoMap,"myClassArray","myClassKfield");//通过本层变量名加上层变量名来定位
for(CheckNullInfo checkNullInfo:checkNullInfoList){
System.out.println(checkNullInfo);
}
} catch (Exception e) {
LOG.error(e.toString());
}
}
}
输出检查解析结果如下:
全量解析对象数据并检查null数据信息
CheckNullInfo{variableName='variableCheckTestClass.myClass.myClassTfield', variableType='java.lang.String', declareClass='test.entity.MyClass', ownerObjectAddr='test.entity.MyClass@7a55af6b'}
CheckNullInfo{variableName='variableCheckTestClass.myClassArray[0].myClassKfield', variableType='java.lang.String', declareClass='test.entity.MyClass', ownerObjectAddr='test.entity.MyClass@598bd2ba'}
CheckNullInfo{variableName='variableCheckTestClass.nestList(0)(0)(1)', variableType='java.lang.String', declareClass='java.util.ArrayList', ownerObjectAddr='[nestList-firstlayer-1, null, nestList-firstlayer-3]'}
CheckNullInfo{variableName='variableCheckTestClass.listWild(0)', variableType='java.lang.String', declareClass='java.util.List', ownerObjectAddr='[null]'}
CheckNullInfo{variableName='variableCheckTestClass.myClass.myClassKfield', variableType='java.lang.String', declareClass='test.entity.MyClass', ownerObjectAddr='test.entity.MyClass@7a55af6b'}
CheckNullInfo{variableName='variableCheckTestClass.stringArray[1]', variableType='java.lang.String', declareClass='[Ljava.lang.String;', ownerObjectAddr='test.entity.VariableCheckTestClass@76a2ddf3'}
CheckNullInfo{variableName='variableCheckTestClass.tvalue[1][1]', variableType='java.lang.String', declareClass='[java.lang.String', ownerObjectAddr='[[Ljava.lang.String;@2d778add'}
CheckNullInfo{variableName='variableCheckTestClass.list(0)(1)', variableType='java.lang.String', declareClass='java.util.ArrayList', ownerObjectAddr='[innerList element first, null, innerList element third]'}
CheckNullInfo{variableName='variableCheckTestClass.listMyClass(0).myClassKfield', variableType='java.lang.String', declareClass='test.entity.MyClass', ownerObjectAddr='test.entity.MyClass@66c92293'}
CheckNullInfo{variableName='variableCheckTestClass.tvalue[0][1]', variableType='java.lang.String', declareClass='[java.lang.String', ownerObjectAddr='[[Ljava.lang.String;@2d778add'}
CheckNullInfo{variableName='variableCheckTestClass.mapMap(firstlayer)(secondlayer-second)', variableType='java.lang.Integer', declareClass='java.util.HashMap', ownerObjectAddr='{secondlayer-third=3, secondlayer-second=null, secondlayer-first=1}'}
基于全量解析的结构查询特定字段是否空值
CheckNullInfo{variableName='variableCheckTestClass.myClassArray[0].myClassKfield', variableType='java.lang.String', declareClass='test.entity.MyClass', ownerObjectAddr='test.entity.MyClass@598bd2ba'}
最后补充下,开发工程采用idea,工程管理使用maven,POM 文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>common-product-toolkits</artifactId>
<groupId>com.shzz.product.toolkits</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>functionality-operation-collections-java</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.googlecode.concurrentlinkedhashmap/concurrentlinkedhashmap-lru -->
<dependency>
<groupId>com.googlecode.concurrentlinkedhashmap</groupId>
<artifactId>concurrentlinkedhashmap-lru</artifactId>
<version>1.4.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.11</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.11</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<!-- start模块在jar包中排除工程配置文件 -->
<excludes>
<exclude>**/*.properties</exclude>
<exclude>**/*.xml</exclude>
</excludes>
</configuration>
</plugin>
<!-- 设置源文件编码方式 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
4 结语
经多种场景测试,该工具代码可实现对象数据的全息检查。以上测试类是有意复杂化处理的,实际项目中,基本是扁平化的实体类结构,字段类型也是基本类型,稍微复杂点的嵌套json 对象也只是常规实体类对象之间的嵌套组合,不涉及泛型申明。该工具的设计初衷是为了检查任意对象,因此在实现上考虑可能出现的场景来完善代码处理逻辑。另外,需求中要求带出null 数据的类型信息这一点其实加大了整体难度,如果不涉及类型推断,实现该功能只需要用递归分析+反射即可,无需针对各种泛型场景进行处理。读者可自行实现。
由于本人水平有限,实现过程中难免有纰漏之处,欢迎大家批评指正,不吝指教。