feat(Java_ConstractUtil):创建项目、单测,进行第一轮简单的测试

This commit is contained in:
huangzj
2020-04-30 18:06:49 +08:00
commit 52648727f8
19 changed files with 1074 additions and 0 deletions
+215
View File
@@ -0,0 +1,215 @@
import bean.ContrastObject;
import com.esotericsoftware.reflectasm.MethodAccess;
import com.google.common.collect.Lists;
import exception.NoSetMethodException;
import exception.TypeMisMatchException;
import handler.CollectionHandler;
import handler.MapHandler;
import handler.OriginHandler;
import handler.SelfHandler;
import util.CheckUtil;
import util.ClassUtil;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static constant.Constant.GET;
import static constant.Constant.SET;
/**
* @author 志军
* @date 2020-1-5 16:00:00
*/
public class ContrastUtil {
//get方法的缓存Map
private static ConcurrentHashMap<String, List<String>> getMethodCache = new ConcurrentHashMap<String, List<String>>();
//set方法的缓存map
private static ConcurrentHashMap<String, List<String>> setMethodCache = new ConcurrentHashMap<String, List<String>>();
/**
* 单对象对比
*
* @param origin 原始对象
* @param target 修改后的对象
* @param <T> 对象类型
* @return 对比结果
*/
@SuppressWarnings("unchecked")
public static <T> ContrastObject<T> contrast(T origin, T target) throws IllegalAccessException, InstantiationException {
//空的情况的判断
if (origin == null || target == null) {
return nullProcessor(origin, target);
}
//提前检查 - 接口或者抽象类
if (!CheckUtil.commonCheck(origin.getClass())) {
return null;
}
//检查类型是否一致
if (!CheckUtil.CheckType(origin, target)) {
throw new TypeMisMatchException("类型不匹配");
}
//先检查get、set方法是否存在.
CheckUtil.checkGetAndSetMethod(origin.getClass());
//初始所有参数
Class<T> tClass = (Class<T>) origin.getClass();
T originChange = tClass.newInstance();
T targetChange = tClass.newInstance();
MethodAccess methodAccess = MethodAccess.get(tClass);
//获取get方法的值进行比较、正常情况下Field都会声明为privatereflectASM对static和private自动跳过,所以用MethodAccess进行处理
List<String> getList = getGetMethod(methodAccess, tClass);
List<String> setList = getSetMethod(methodAccess, tClass);
//循环所有的MethodAccess判断是否发生变化
boolean changed = forContrast(getList, methodAccess, origin, target, setList, originChange, targetChange);
if (!changed) {
return null;
}
return setContrastObject(origin, target, originChange, targetChange);
}
private static <T> ContrastObject<T> nullProcessor(T origin, T target) {
if (origin == null && target == null) {
return null;
}
if (origin == null) {
return setContrastObject(null, target, null, target);
}
return setContrastObject(origin, null, origin, null);
}
private static <T> boolean forContrast(List<String> getList, MethodAccess methodAccess, T origin, T target, List<String> setList,
T originChange, T targetChange) throws InstantiationException,
IllegalAccessException {
boolean changed = false;
for (String methodName : getList) {
//reflectASM会对基本类型进行装箱(比如说int返回Integer),所以这里要考虑的就是自定义类型怎么比较的问题
Object originValue = methodAccess.invoke(origin, methodName);
Object targetValue = methodAccess.invoke(target, methodName);
String setName = null;
for (String setMethodName : setList) {
if (setMethodName.substring(3).equals(methodName.substring(3))) {
setName = setMethodName;
break;
}
}
if (setName == null) {
throw new NoSetMethodException();
}
if (originValue == null && targetValue == null) {
continue;
}
//对比 + 设值
changed = changed || contrastValue(originValue, targetValue, originChange, targetChange, methodAccess, setName);
}
return changed;
}
private static <T> ContrastObject<T> setContrastObject(T origin, T target, T originChange, T targetChange) {
ContrastObject<T> contrastObject = new ContrastObject<>();
contrastObject.setOrigin(origin);
contrastObject.setTarget(target);
contrastObject.setOriginChange(originChange);
contrastObject.setTargetChange(targetChange);
return contrastObject;
}
/**
* @param originValue get方法拿到的原始的值
* @param targetValue get方法拿到的变化的值
* @param originChange 新创建的原始变化对象
* @param targetChange 新创建的改变变化对象
* @param methodAccess reflectASM类
* @param setName set方法名
* @throws IllegalAccessException 异常
* @throws InstantiationException 异常
*/
private static boolean contrastValue(Object originValue, Object targetValue, Object originChange, Object targetChange,
MethodAccess methodAccess, String setName) throws IllegalAccessException, InstantiationException {
//处理Collection
if (originValue instanceof Collection || targetValue instanceof Collection) {
return CollectionHandler.contract(originChange, targetChange, originValue, targetValue, methodAccess, setName);
}
//Map的处理
if (originValue instanceof Map || targetValue instanceof Map) {
return MapHandler.putValue(originChange, targetChange, originValue, targetValue, methodAccess, setName);
}
//非自定义类的处理
Boolean result = OriginHandler.contract(originChange, targetChange, originValue, targetValue, methodAccess, setName);
if (null != result) {
return result;
}
//自定义类型的处理
return SelfHandler.contract(originChange, targetChange, originValue, targetValue, methodAccess, setName);
}
/**
* 获取Get方法集合
*
* @param methodAccess MethodAccess
* @param tClass 类.class
* @param <T> T
* @return 方法名
*/
private static <T> List<String> getSetMethod(MethodAccess methodAccess, Class<T> tClass) {
if (setMethodCache.get(tClass.getName()) != null) {
return setMethodCache.get(tClass.getName());
}
List<String> methodNameList = Lists.newArrayList();
List<Method> methods = ClassUtil.getAllMethodWithSuper(tClass);
String[] methodNames = methodAccess.getMethodNames();
return buildMethodList(SET, methodNames, methods, methodNameList, setMethodCache, tClass);
}
private static <T> List<String> buildMethodList(String type, String[] methodNames, List<Method> methods, List<String> methodNameList,
ConcurrentHashMap<String, List<String>> methodCache, Class<T> tClass) {
for (String methodName : methodNames) {
if (methodName.startsWith(type)) {
for (Method method : methods) {
if (method.getName().equals(methodName)) {
methodNameList.add(methodName);
break;
}
}
}
}
methodCache.put(tClass.getName(), methodNameList);
return methodNameList;
}
/**
* 获取Set方法集合
*
* @param methodAccess MethodAccess
* @param tClass 类.class
* @param <T> T
* @return 方法名
*/
private static <T> List<String> getGetMethod(MethodAccess methodAccess, Class<T> tClass) {
if (getMethodCache.get(tClass.getName()) != null) {
return getMethodCache.get(tClass.getName());
}
List<String> methodNameList = Lists.newArrayList();
String[] methodNames = methodAccess.getMethodNames();
List<Method> methods = ClassUtil.getAllMethodWithSuper(tClass);
return buildMethodList(GET, methodNames, methods, methodNameList, getMethodCache, tClass);
}
}
+81
View File
@@ -0,0 +1,81 @@
package bean;
import java.util.Objects;
/**
* 返回的对比对象的包装
*
*/
public class ContrastObject<T> {
/**
* 原始的java对象
*/
private T origin;
/**
* 修改后的java对象
*/
private T target;
/**
* 原始的java对象修改的部分
*/
private T originChange;
/**
* 修改后的java对象修改的部分
*/
private T targetChange;
public T getOrigin() {
return origin;
}
public void setOrigin(T origin) {
this.origin = origin;
}
public T getTarget() {
return target;
}
public void setTarget(T target) {
this.target = target;
}
public T getOriginChange() {
return originChange;
}
public void setOriginChange(T originChange) {
this.originChange = originChange;
}
public T getTargetChange() {
return targetChange;
}
public void setTargetChange(T targetChange) {
this.targetChange = targetChange;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ContrastObject<?> that = (ContrastObject<?>) o;
return getOrigin().equals(that.getOrigin()) &&
getTarget().equals(that.getTarget()) &&
getOriginChange().equals(that.getOriginChange()) &&
getTargetChange().equals(that.getTargetChange());
}
@Override
public int hashCode() {
return Objects.hash(getOrigin(), getTarget(), getOriginChange(), getTargetChange());
}
}
+9
View File
@@ -0,0 +1,9 @@
package constant;
public class Constant {
public static final String GET = "get";
public static final String SET = "set";
public static final String WELL_NUMBER = "#";
}
@@ -0,0 +1,24 @@
package exception;
public class NoGetMethodException extends RuntimeException{
public NoGetMethodException() {
super();
}
public NoGetMethodException(String message) {
super(message);
}
public NoGetMethodException(String message, Throwable cause) {
super(message, cause);
}
public NoGetMethodException(Throwable cause) {
super(cause);
}
protected NoGetMethodException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
@@ -0,0 +1,24 @@
package exception;
public class NoSetMethodException extends RuntimeException {
public NoSetMethodException() {
super();
}
public NoSetMethodException(String message) {
super(message);
}
public NoSetMethodException(String message, Throwable cause) {
super(message, cause);
}
public NoSetMethodException(Throwable cause) {
super(cause);
}
protected NoSetMethodException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
@@ -0,0 +1,24 @@
package exception;
public class TypeMisMatchException extends RuntimeException {
public TypeMisMatchException() {
super();
}
public TypeMisMatchException(String message) {
super(message);
}
public TypeMisMatchException(String message, Throwable cause) {
super(message, cause);
}
public TypeMisMatchException(Throwable cause) {
super(cause);
}
protected TypeMisMatchException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2020.
* made by 志军
*/
package handler;
import com.esotericsoftware.reflectasm.MethodAccess;
import org.apache.commons.collections4.CollectionUtils;
import util.CollectionUtil;
import java.util.Collection;
/**
* 处理Collection类型的对比,必须重写equals和hashCode方法
*
* @author 志军
*/
public class CollectionHandler {
/**
* 对比组合Collection差异的部分
*
* @param originChange 原始的变化部分的对象
* @param targetChange 修改后变化部分的对象
* @param originValue 原始的对象
* @param targetValue 修改后的对象
* @param methodAccess MethodAccess
* @param setName set方法名
* @return 是否变化
* @throws IllegalAccessException 异常
* @throws InstantiationException 异常
*/
public static Boolean contract(Object originChange, Object targetChange, Object originValue, Object targetValue,
MethodAccess methodAccess, String setName) throws IllegalAccessException, InstantiationException {
boolean changed;
Class returnValue = originValue.getClass();
//判断Collection是否相等
if (CollectionUtils.isEqualCollection((Collection) originValue, (Collection) targetValue)) {
return false;
}
Object originCollection = CollectionUtil.buildDiffCollection(originValue, targetValue, returnValue);
Object changeCollection = CollectionUtil.buildDiffCollection(targetValue, originValue, returnValue);
changed = CollectionUtils.isNotEmpty((Collection) originCollection) || CollectionUtils.isNotEmpty((Collection) changeCollection);
if (changed) {
methodAccess.invoke(originChange, setName, originCollection);
methodAccess.invoke(targetChange, setName, changeCollection);
}
return changed;
}
}
+69
View File
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2020.
* made by 志军
*/
package handler;
import com.esotericsoftware.reflectasm.MethodAccess;
import org.apache.commons.collections.CollectionUtils;
import util.MapUtil;
import util.SetUtil;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 处理Map对应的对比
*
* @author 志军
*/
public class MapHandler {
@SuppressWarnings("unchecked")
public static boolean putValue(Object originChange, Object targetChange, Object originValue, Object targetValue,
MethodAccess methodAccess, String setName) throws IllegalAccessException, InstantiationException {
//参数前置
boolean changed = false;
Set<Map.Entry> originEntry = ((Map) originValue).entrySet();
Set<Map.Entry> targetEntry = ((Map) targetValue).entrySet();
Map originMap = (Map) originValue.getClass().newInstance();
Map targetMap = (Map) targetValue.getClass().newInstance();
//处理key一样的value比较.
for (Map.Entry entry : originEntry) {
Object key = entry.getKey();
for (Map.Entry tEntry : targetEntry) {
if (tEntry.getKey().equals(key)) {
//每一个key都进行比较,如果key相等则比较对应的value,这个value需要重写hashcode和equals方法
changed = changed || MapHandler.putValue(originChange, targetChange, originMap, targetMap, key);
break;
}
}
}
//得到不一致的key的集合.
List<Map.Entry> originList = SetUtil.findUnLikeValue(originEntry, targetEntry);
List<Map.Entry> changeList = SetUtil.findUnLikeValue(originEntry, targetEntry);
//计算changed的值
changed = changed || !CollectionUtils.isEmpty(originList) || CollectionUtils.isEmpty(changeList);
//把不一致的值设置会新的Map中
MapUtil.putIfNotEmpty(originMap, originList);
MapUtil.putIfNotEmpty(targetMap, changeList);
//对对象进行设值
methodAccess.invoke(originChange, setName, originValue);
methodAccess.invoke(targetChange, setName, targetValue);
return changed;
}
private static boolean putValue(Object originChange, Object targetChange, Map<Object, Object> originMap,
Map<Object, Object> targetMap, Object key) {
if (originChange.equals(targetChange)) {
return false;
}
originMap.put(key, originChange);
targetMap.put(key, targetChange);
return true;
}
}
+47
View File
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2020.
* made by 志军
*/
package handler;
import com.esotericsoftware.reflectasm.MethodAccess;
/**
* 处理除Iterable和Map之外的原始类型
*
* @author 志军
*/
public class OriginHandler {
public static Boolean contract(Object originChange, Object targetChange, Object originValue, Object targetValue,
MethodAccess methodAccess, String setName) {
Class returnValue = originValue == null ? targetValue.getClass() : originValue.getClass();
try {
if (returnValue.getClassLoader() == null) {
return contractValue(originChange, targetChange, originValue, targetValue, methodAccess, setName);
} else {
//用来判断是否进行了比较
return null;
}
} catch (NullPointerException ex) {
//这边是处理Object类型
return contractValue(originChange, targetChange, originValue, targetValue, methodAccess, setName);
}
}
private static boolean contractValue(Object originChange, Object targetChange, Object originValue, Object targetValue,
MethodAccess methodAccess, String setName) {
boolean result = (originValue == null ? targetValue.equals(originChange) : originValue.equals(targetValue));
if (originValue == targetValue || result) {
return false;
} else {
methodAccess.invoke(originChange, setName, originValue);
methodAccess.invoke(targetChange, setName, targetValue);
return true;
}
}
}
+25
View File
@@ -0,0 +1,25 @@
/*
* Copyright (c) 2020.
* made by 志军
*/
package handler;
import com.esotericsoftware.reflectasm.MethodAccess;
/**
* 处理自定义的类,必须重写hashCode和equals方法,不然会出现对比偏差.<p>
* @author 志军
*/
public class SelfHandler {
public static Boolean contract(Object originChange, Object targetChange, Object originValue, Object targetValue,
MethodAccess methodAccess, String setName) {
if (!originChange.equals(targetChange)) {
methodAccess.invoke(originChange, setName, originValue);
methodAccess.invoke(targetChange, setName, targetValue);
return true;
}
return false;
}
}
+81
View File
@@ -0,0 +1,81 @@
package util;
import com.google.common.collect.Lists;
import exception.NoGetMethodException;
import exception.NoSetMethodException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Vector;
import static constant.Constant.GET;
import static constant.Constant.SET;
/**
* @author 志军
* @date 2020-1-5 16:00:00
*/
public class CheckUtil {
private static Vector<String> vector = new Vector<>();
/**
* 提前检查是否包含了get、set方法
* TODO 没有考虑is方法
*
* @param zClass Class类
*/
public static void checkGetAndSetMethod(Class zClass) {
if (vector.contains(zClass.getName())) {
return;
}
List<Field> fieldList = ClassUtil.getAllFieldWithSuper(zClass);
List<Method> methodList = ClassUtil.getAllMethodWithSuper(zClass);
List<String> methodNameList = getMethodNameList(methodList);
for (Field field : fieldList) {
String fieldName = field.getName();
final String s = String.valueOf(fieldName.charAt(0)).toUpperCase();
String setMethodName = SET + s + fieldName.substring(1);
String getMethodName = GET + s + fieldName.substring(1);
if (!methodNameList.contains(getMethodName)) {
throw new NoGetMethodException();
}
//这边加上了set的参数校验
if (!methodNameList.contains(setMethodName) || !setMethodParamCheck(field, setMethodName, methodList)) {
throw new NoSetMethodException();
}
}
vector.add(zClass.getName());
}
private static boolean setMethodParamCheck(Field field, String setMethodName, List<Method> methodList) {
for (Method method : methodList) {
if (method.getName().equals(setMethodName) && method.getParameterTypes()[0] == field.getType()) {
return true;
}
}
return false;
}
private static List<String> getMethodNameList(List<Method> methodList) {
List<String> methodNameList = Lists.newArrayList();
for (Method method : methodList) {
methodNameList.add(method.getName());
}
return methodNameList;
}
/**
* 检查是否为抽象类或者接口
*/
public static boolean commonCheck(Class<?> aClass) {
return !Modifier.isAbstract(aClass.getModifiers()) && !Modifier.isInterface(aClass.getModifiers());
}
public static <T> boolean CheckType(T origin, T target) {
return origin.getClass() == target.getClass();
}
}
+112
View File
@@ -0,0 +1,112 @@
package util;
import com.esotericsoftware.reflectasm.MethodAccess;
import com.google.common.collect.Lists;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import static constant.Constant.*;
/**
* @author 志军
* @date 2020-1-5 16:00:00
* 其实你不用担心类似 ##<code>fieldCache.get(zClass.getName())</code>这样的代码在一个方法里面写了很多遍会处理多次
* <p>可以了解一下##方法内联
*/
public class ClassUtil {
/**
* 类名#方法 - 方法索引 缓存
*/
private static ConcurrentHashMap<String, Integer> cache = new ConcurrentHashMap<String, Integer>();
/**
* 类 - Field数组 缓存
*/
private static ConcurrentHashMap<String, List<Field>> fieldCache = new ConcurrentHashMap<String, List<Field>>();
/**
* 类 - method数组 缓存
*/
private static ConcurrentHashMap<String, List<Method>> methodCache = new ConcurrentHashMap<String, List<Method>>();
/**
* 获取get方法
*
* @param fieldName 类属性名称
* @param zClass 类.class
* @return GetMethod
*/
public static Integer getGetMethod(String fieldName, Class zClass) {
//获取下标的方式非常方便,本质上,如果使用getDeclareMethod方法,还需要考虑继承的问题
MethodAccess access = MethodAccess.get(zClass);
String getMethodName = GET + String.valueOf(fieldName.charAt(0)).toUpperCase() + fieldName.substring(1);
String keyName = zClass.getName() + WELL_NUMBER + getMethodName;
if (cache.get(keyName) == null) {
cache.put(keyName, access.getIndex(getMethodName));
return access.getIndex(getMethodName);
}
return cache.get(keyName);
}
/**
* 获取set方法
*
* @param fieldName 类属性名称
* @param zClass 类.class
* @return SetMethod
*/
public static Integer getSetMethod(String fieldName, Class zClass) {
String setMethodName = SET + String.valueOf(fieldName.charAt(0)).toUpperCase() + fieldName.substring(1);
String keyName = zClass.getName() + WELL_NUMBER + setMethodName;
MethodAccess access = MethodAccess.get(zClass);
if (cache.get(keyName) != null) {
return cache.get(keyName);
}
cache.put(keyName, access.getIndex(setMethodName));
return access.getIndex(setMethodName);
}
static List<Field> getAllFieldWithSuper(Class zClass) {
if (fieldCache.get(zClass.getName()) != null) {
return fieldCache.get(zClass.getName());
}
List<Field> fieldList = Lists.newArrayList();
Class tempClass = zClass;
while (tempClass != null) {
fieldList.addAll(Arrays.asList(tempClass.getDeclaredFields()));
tempClass = tempClass.getSuperclass();
}
fieldCache.put(zClass.getName(), fieldList);
return fieldList;
}
public static List<Method> getAllMethodWithSuper(Class zClass) {
if (methodCache.get(zClass.getName()) != null) {
return methodCache.get(zClass.getName());
}
Class tempClass = zClass;
List<Method> methodList = Lists.newArrayList();
while (tempClass != null) {
methodList.addAll(Arrays.asList(tempClass.getDeclaredMethods()));
tempClass = tempClass.getSuperclass();
}
methodCache.put(zClass.getName(), methodList);
return methodList;
}
}
+32
View File
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2020.
* made by 志军
*/
package util;
import java.util.Collection;
/**
* Collection类型的处理工具
* @author 志军
*/
public class CollectionUtil {
@SuppressWarnings("unchecked")
public static Collection buildDiffCollection(Object originValue, Object targetValue, Class returnValue) throws IllegalAccessException,
InstantiationException {
Object collection = returnValue.newInstance();
for (Object value : (Collection) originValue) {
if (((Collection) targetValue).contains(value)) {
continue;
}
((Collection) collection).add(value);
}
return (Collection) collection;
}
}
+34
View File
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2020.
* made by 志军
*/
package util;
import org.apache.commons.collections.CollectionUtils;
import java.util.List;
import java.util.Map;
/**
* Map工具类
* @author 志军
*/
public class MapUtil {
/**
* 把List中Entry对应的key、value设置到新的Map中
* @param map 新的Map
* @param changeList 变化的List
*/
public static void putIfNotEmpty(Map<Object, Object> map, List<Map.Entry> changeList) {
if (CollectionUtils.isEmpty(changeList)) {
return;
}
for (Map.Entry entry : changeList) {
map.put(entry.getKey(), entry.getValue());
}
}
}
+37
View File
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2020.
* made by 志军
*/
package util;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.Set;
/**
* Set工具类
* @author 志军
*/
public class SetUtil {
/**
*
* @param origin 原始Set
* @param target 目标Set
* @param <T> Set类型
* @return 返回origin中不被包含在target的元素集合
*/
public static <T> List<T> findUnLikeValue(Set<T> origin, Set<T> target) {
List<T> unLikeList = Lists.newArrayList();
for (T t : origin) {
if (target.contains(t)) {
continue;
}
unLikeList.add(t);
}
return unLikeList;
}
}
+4
View File
@@ -0,0 +1,4 @@
# 工具说明
# 更新日志
2020/4/30: 创建项目、单测,进行第一轮简单的测试
+15
View File
@@ -0,0 +1,15 @@
import bean.ContrastObject;
public class ContractTest {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
SimpleObj s1 = new SimpleObj();
s1.setM((float) 2.2);
s1.setY("55555");
ContrastObject<SimpleObj> sim = ContrastUtil.contrast(new SimpleObj(), s1);
System.out.print(sim.getTargetChange().m + " " + sim.getTargetChange().x + " " + sim.getTargetChange().y);
}
}
+43
View File
@@ -0,0 +1,43 @@
import java.util.Date;
public class SimpleObj {
int x;
String y;
float m;
Date date;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public String getY() {
return y;
}
public void setY(String y) {
this.y = y;
}
public float getM() {
return m;
}
public void setM(float m) {
this.m = m;
}
}