From 689a9419e8ba92cd8225f6e8869940407d52e3f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BF=97=E5=86=9B?= <851516902@qq.com> Date: Sun, 17 May 2026 09:32:34 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Java_ConstractUtil.iml | 8 + readme.md | 163 ++++++++++++++++--- src/main/java/AbstractContrastUtil.java | 38 ++++- src/main/java/ContrastUtil.java | 76 +++++++-- src/main/java/ContrastUtilExample.java | 11 ++ src/main/java/bean/ContrastObject.java | 8 +- src/main/java/constant/Constant.java | 1 - src/main/java/handler/CollectionHandler.java | 6 +- src/main/java/handler/MapHandler.java | 25 +-- src/main/java/handler/OriginHandler.java | 24 ++- src/main/java/handler/SelfHandler.java | 11 ++ src/main/java/util/CheckUtil.java | 15 +- src/main/java/util/ClassUtil.java | 50 ------ src/main/java/util/CollectionUtil.java | 5 +- src/main/java/util/MapUtil.java | 2 +- src/main/test/ContractTest.java | 14 +- 16 files changed, 321 insertions(+), 136 deletions(-) create mode 100644 Java_ConstractUtil.iml diff --git a/Java_ConstractUtil.iml b/Java_ConstractUtil.iml new file mode 100644 index 0000000..3d7794c --- /dev/null +++ b/Java_ConstractUtil.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/readme.md b/readme.md index 0e27038..1202261 100644 --- a/readme.md +++ b/readme.md @@ -1,27 +1,150 @@ # 我的公众号 ![](.readme/B5F8E89A-CE33-4a78-A461-E9014B10A3D9.png) -# 工具说明 -本工程提供ContrastUtil.java作为通用同类型对象处理工具类,处理规则如下(有没有测试到的bug,欢迎指教.qq:851516902): - - 1.Collection和Map的处理当前类只会处理一层,也就是说比如List<泛型>:这个泛型类型会直接调用equals进行处理,可能照成结果的不准确(可以通过Abstract类进行重写) - 2.如果比较的不是结构体而是基本类型,返回兼容结果 - 3.比较的两个对象类型必须一致 - 4.没有处理Collection的入参和Map的入参,后续会加上 - 5.入参可以为空 - 6.自定义类型没有Get、Set方法会进行报错。不会比较 - 7、自定义类型必须重写equals和hashcode方法,否则不会比较值,而是比较对象地址(这边提供一种自处理的方式,可参考lombok的处理,直接修改AST,这样就算用户不重写equals也会直接比较对应值) - 8、 - -本工程提供AbstractContrastUtil.java作为对象对比抽象基类,提供isSpecialType的判断,可以在对象属性比较的时候进行对应类型的拦截处理,并使用者需要实现Collection、Map、基本类型、自定义类型的处理 -提供最大的自定义性,因为对应的类型太多,ContractUtil没办法一并处理,所以提供对应抽象类。抽象类的继承示例可参考:ContrastUtilExample.java。该类的实现主要参考ContractUtil.java。 +# Java_ConstractUtil -本工程对于一些抽象类的方法没有非常详细的注释,可直接参考Contract.java的实现处理。 +通用的Java同类型对象对比工具类,用于比较两个同类型对象的属性差异,并返回变化的部分。 -本工程比较通用而且直接使用反射的方式,在处理速度上面会有一定的限制,后期可修改为内省的方式处理,且处理情况过多,没有覆盖所有的测试场景,希望读者可以提出宝贵的意见和建议。 - -# 更新日志 -2020/4/30: 创建项目、单测,进行第一轮简单的测试 +--- +## 快速开始 -2020/5/9:添加测试方法,修改bug(反射处理null出现空指针的问题比较多),创建抽象类和继承使用示例。添加readme修改记录 \ No newline at end of file +### Maven依赖 + +```xml + + Java_ConstractUtil + Java_ConstractUtil + 1.0.0 + +``` + +### 基本使用 + +```java +// 比较两个同类型对象 +SimpleObj origin = new SimpleObj(); +origin.setX(1); +origin.setY("hello"); + +SimpleObj target = new SimpleObj(); +target.setX(2); +target.setY("hello"); + +ContrastObject result = ContrastUtil.contrast(origin, target); +if (result != null) { + // result.getOriginChange() -> 原始对象中发生变化的属性 (x=1) + // result.getTargetChange() -> 修改后对象中发生变化的属性 (x=2) + System.out.println(result.getOriginChange().getX()); // 1 + System.out.println(result.getTargetChange().getX()); // 2 +} +``` + +--- + +## 工具说明 + +### ContrastUtil(通用对比工具) + +`ContrastUtil` 是默认的对象对比实现类,提供静态方法 `contrast(T origin, T target)` 进行对象对比。 + +**处理规则:** + +1. **基本类型和包装类型**:直接使用 `equals` 进行比较(如 `int`、`String`、`Date` 等) +2. **Collection类型**:比较两个集合的差异,返回差集。仅处理一层,泛型内的元素直接调用 `equals` 比较 +3. **Map类型**:比较两个Map的差异,包括相同key的value比较和不同key的差异 +4. **自定义类型**:调用 `equals` 方法进行比较,**要求重写 `equals` 和 `hashCode` 方法**,否则会比较对象地址 +5. **类型一致性**:比较的两个对象类型必须一致,否则抛出 `TypeMisMatchException` +6. **空值处理**:入参可以为 `null`,两者都为 `null` 返回 `null`,其中一个为 `null` 则返回非空一方的结果 +7. **Getter/Setter检查**:自定义类型必须有完整的 `get`/`set` 方法,否则抛出 `NoGetMethodException` / `NoSetMethodException` +8. **抽象类和接口**:不支持抽象类和接口的实例对比,会直接返回 `null` + +**返回值说明:** + +`ContrastObject` 包含四个字段: +- `origin`:原始对象(完整) +- `target`:修改后的对象(完整) +- `originChange`:原始对象中**发生变化**的属性(新实例,仅包含变化的字段) +- `targetChange`:修改后对象中**发生变化**的属性(新实例,仅包含变化的字段) + +无变化时返回 `null`。 + +--- + +### AbstractContrastUtil(抽象基类) + +`AbstractContrastUtil` 提供最大的自定义性,使用者可以通过继承并实现抽象方法来定制各种类型的对比逻辑。 + +**需要实现的抽象方法:** + +| 方法 | 说明 | +|------|------| +| `originContract()` | 基本类型和包装类型的对比逻辑 | +| `selfContract()` | 自定义类型的对比逻辑 | +| `mapContract()` | Map类型的对比逻辑 | +| `collectionContract()` | Collection类型的对比逻辑 | +| `specialContract()` | 特殊类型的自定义处理逻辑 | +| `isSpecialType()` | 判断是否为特殊类型(优先于Collection/Map判断) | +| `nullProcessor()` | 入参为null的处理逻辑 | +| `originProcessor()` | 基本类型入参的处理逻辑 | +| `setContrastObject()` | 组装返回结果对象 | + +**使用示例:** 参考 [ContrastUtilExample.java](src/main/java/ContrastUtilExample.java) + +--- + +## 项目结构 + +``` +src/main/java/ +├── ContrastUtil.java # 默认对比工具实现 +├── AbstractContrastUtil.java # 抽象对比基类 +├── ContrastUtilExample.java # 抽象基类使用示例 +├── bean/ +│ └── ContrastObject.java # 对比结果包装类 +├── constant/ +│ └── Constant.java # 常量定义 +├── exception/ +│ ├── NoGetMethodException.java # 缺少getter方法异常 +│ ├── NoSetMethodException.java # 缺少setter方法异常 +│ └── TypeMisMatchException.java # 类型不匹配异常 +├── handler/ +│ ├── CollectionHandler.java # Collection类型处理器 +│ ├── MapHandler.java # Map类型处理器 +│ ├── OriginHandler.java # 基本类型处理器 +│ └── SelfHandler.java # 自定义类型处理器 +└── util/ + ├── CheckUtil.java # 类型检查工具 + ├── ClassUtil.java # 类反射工具 + ├── CollectionUtil.java # 集合差异计算工具 + ├── MapUtil.java # Map工具 + └── SetUtil.java # Set工具 +``` + +--- + +## 依赖 + +| 依赖 | 版本 | 用途 | +|------|------|------| +| commons-lang3 | 3.9 | 通用工具 | +| commons-collections4 | 4.4 | 集合工具 | +| guava | 28.2-jre | Google工具库 | +| reflectasm | 1.11.0 | 高性能反射 | + +--- + +## 注意事项 + +1. Collection和Map仅处理一层,嵌套的泛型类型直接调用 `equals` 比较,可能导致结果不准确。如需深度对比,请使用 `AbstractContrastUtil` 自定义处理逻辑 +2. 自定义类型**必须重写 `equals` 和 `hashCode` 方法**,否则会比较对象地址而非值 +3. 当前使用 reflectASM 进行反射调用,性能优于标准反射,但仍有优化空间 +4. 暂不支持 `is` 前缀的 boolean getter 方法(如 `isActive()`),后续版本会支持 + +--- + +## 更新日志 + +- **2020/4/30**:创建项目,完成初始实现和单元测试 +- **2020/5/9**:添加测试方法,修复反射处理null的空指针问题,创建抽象类和继承使用示例 +- **2026/5/17**:修复多个bug(MapHandler参数错误、OriginHandler空值比较逻辑、ContrastObject NPE风险等),优化代码实现,补全注释和文档 \ No newline at end of file diff --git a/src/main/java/AbstractContrastUtil.java b/src/main/java/AbstractContrastUtil.java index 15062ea..b294d81 100644 --- a/src/main/java/AbstractContrastUtil.java +++ b/src/main/java/AbstractContrastUtil.java @@ -121,6 +121,19 @@ public abstract class AbstractContrastUtil { } + /** + * 遍历所有getter方法,逐一对比属性值,并将差异设入change对象 + * + * @param getList getter方法名列表 + * @param methodAccess reflectASM方法访问器 + * @param origin 原始对象 + * @param target 修改后的对象 + * @param setList setter方法名列表 + * @param originChange 原始对象变化部分 + * @param targetChange 修改后对象变化部分 + * @param 对象类型 + * @return 是否有属性发生变化 + */ private boolean forContrast(List getList, MethodAccess methodAccess, T origin, T target, List setList, T originChange, T targetChange) throws Exception { boolean changed = false; @@ -151,35 +164,39 @@ public abstract class AbstractContrastUtil { } + /** + * 根据属性值类型分发到对应的处理器进行对比 + * 优先检查特殊类型,然后依次处理Collection、Map、基本类型和自定义类型 + */ private boolean contrastValue(Object originValue, Object targetValue, Object originChange, Object targetChange, MethodAccess methodAccess, String setName) throws Exception { - //特殊拦截类型的处理 if (isSpecialType(originValue, targetValue)) { return specialContract(originChange, targetChange, originValue, targetValue, methodAccess, setName); } - //处理Collection - if (originValue instanceof Collection || targetValue instanceof Collection) { + if (originValue instanceof Collection && targetValue instanceof Collection) { return collectionContract(originChange, targetChange, originValue, targetValue, methodAccess, setName); } - //Map的处理 - if (originValue instanceof Map || targetValue instanceof Map) { + if (originValue instanceof Map && targetValue instanceof Map) { return mapContract(originChange, targetChange, originValue, targetValue, methodAccess, setName); } - //非自定义类的处理 + if (originValue instanceof Collection || targetValue instanceof Collection + || originValue instanceof Map || targetValue instanceof Map) { + return selfContract(originChange, targetChange, originValue, targetValue, methodAccess, setName); + } + Boolean result = originContract(originChange, targetChange, originValue, targetValue, methodAccess, setName); if (null != result) { return result; } - //自定义类型的处理 return selfContract(originChange, targetChange, originValue, targetValue, methodAccess, setName); } /** - * 获取Set方法集合 + * 获取Get方法集合 * * @param methodAccess MethodAccess * @param tClass 类.class @@ -199,7 +216,7 @@ public abstract class AbstractContrastUtil { /** - * 获取Get方法集合 + * 获取Set方法集合 * * @param methodAccess MethodAccess * @param tClass 类.class @@ -218,6 +235,9 @@ public abstract class AbstractContrastUtil { } + /** + * 构建指定类型(get/set)的方法名列表,并缓存 + */ private static List buildMethodList(String type, String[] methodNames, List methods, List methodNameList, ConcurrentHashMap> methodCache, Class tClass) { for (String methodName : methodNames) { diff --git a/src/main/java/ContrastUtil.java b/src/main/java/ContrastUtil.java index 689c032..ecb8323 100644 --- a/src/main/java/ContrastUtil.java +++ b/src/main/java/ContrastUtil.java @@ -24,7 +24,7 @@ import static constant.Constant.SET; * @description 通用的对象比较基本实现类 * @date 2020-1-5 16:00:00 */ -class ContrastUtil { +public class ContrastUtil { /** @@ -46,7 +46,7 @@ class ContrastUtil { * @return 对比结果 */ @SuppressWarnings("unchecked") - static ContrastObject contrast(T origin, T target) throws IllegalAccessException, InstantiationException { + public static ContrastObject contrast(T origin, T target) throws IllegalAccessException, InstantiationException { //空的情况的判断 if (origin == null || target == null) { return nullProcessor(origin, target); @@ -88,6 +88,14 @@ class ContrastUtil { return setContrastObject(origin, target, originChange, targetChange); } + /** + * 处理基本类型和包装类型的对比(由Bootstrap ClassLoader加载的类) + * + * @param origin 原始对象 + * @param target 修改后的对象 + * @param 对象类型 + * @return 对比结果,无变化返回null + */ @SuppressWarnings("unchecked") private static ContrastObject originProcessor(T origin, T target) { if (origin == target) { @@ -96,6 +104,14 @@ class ContrastUtil { return new ContrastObject(origin, target, origin, target); } + /** + * 处理入参为null的情况 + * + * @param origin 原始对象 + * @param target 修改后的对象 + * @param 对象类型 + * @return 对比结果,两者都为null返回null,否则返回包含非null一方的结果 + */ private static ContrastObject nullProcessor(T origin, T target) { if (origin == null && target == null) { return null; @@ -108,6 +124,19 @@ class ContrastUtil { return setContrastObject(origin, null, origin, null); } + /** + * 遍历所有getter方法,逐一对比属性值,并将差异设入change对象 + * + * @param getList getter方法名列表 + * @param methodAccess reflectASM方法访问器 + * @param origin 原始对象 + * @param target 修改后的对象 + * @param setList setter方法名列表 + * @param originChange 原始对象变化部分 + * @param targetChange 修改后对象变化部分 + * @param 对象类型 + * @return 是否有属性发生变化 + */ private static boolean forContrast(List getList, MethodAccess methodAccess, T origin, T target, List setList, T originChange, T targetChange) throws InstantiationException, IllegalAccessException { @@ -139,6 +168,16 @@ class ContrastUtil { return changed; } + /** + * 组装对比结果对象 + * + * @param origin 原始对象 + * @param target 修改后的对象 + * @param originChange 原始对象中发生变化的部分 + * @param targetChange 修改后对象中发生变化的部分 + * @param 对象类型 + * @return 对比结果 + */ private static ContrastObject setContrastObject(T origin, T target, T originChange, T targetChange) { ContrastObject contrastObject = new ContrastObject<>(); contrastObject.setOrigin(origin); @@ -149,40 +188,45 @@ class ContrastUtil { } /** + * 根据属性值类型分发到对应的处理器进行对比 + * 支持Collection、Map、基本类型和自定义类型的对比 + * * @param originValue get方法拿到的原始的值 * @param targetValue get方法拿到的变化的值 * @param originChange 新创建的原始变化对象 * @param targetChange 新创建的改变变化对象 * @param methodAccess reflectASM类 * @param setName set方法名 + * @return 该属性是否发生变化 * @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) { + if (originValue instanceof Collection && targetValue instanceof Collection) { return CollectionHandler.contract(originChange, targetChange, originValue, targetValue, methodAccess, setName); } - //Map的处理 - if (originValue instanceof Map || targetValue instanceof Map) { + if (originValue instanceof Map && targetValue instanceof Map) { return MapHandler.putValue(originChange, targetChange, originValue, targetValue, methodAccess, setName); } - //非自定义类的处理 + if (originValue instanceof Collection || targetValue instanceof Collection + || originValue instanceof Map || targetValue instanceof Map) { + return SelfHandler.contract(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方法集合 + * 获取Set方法集合 * * @param methodAccess MethodAccess * @param tClass 类.class @@ -200,6 +244,18 @@ class ContrastUtil { return buildMethodList(SET, methodNames, methods, methodNameList, setMethodCache, tClass); } + /** + * 构建指定类型(get/set)的方法名列表,并缓存 + * + * @param type 方法类型前缀(get或set) + * @param methodNames reflectASM返回的所有方法名 + * @param methods 反射获取的所有Method列表 + * @param methodNameList 用于收集方法名的列表 + * @param methodCache 方法名缓存Map + * @param tClass 类.class + * @param T + * @return 过滤后的方法名列表 + */ private static List buildMethodList(String type, String[] methodNames, List methods, List methodNameList, ConcurrentHashMap> methodCache, Class tClass) { for (String methodName : methodNames) { @@ -217,7 +273,7 @@ class ContrastUtil { } /** - * 获取Set方法集合 + * 获取Get方法集合 * * @param methodAccess MethodAccess * @param tClass 类.class diff --git a/src/main/java/ContrastUtilExample.java b/src/main/java/ContrastUtilExample.java index 3829766..668cb56 100644 --- a/src/main/java/ContrastUtilExample.java +++ b/src/main/java/ContrastUtilExample.java @@ -5,9 +5,17 @@ import handler.MapHandler; import handler.OriginHandler; /** + * AbstractContrastUtil的使用示例 + * 该示例直接复用了ContrastUtil中的CollectionHandler、MapHandler、OriginHandler处理器 + * 自定义类型(selfContract)和特殊类型(specialContract)的处理返回false,表示不进行深度对比 + * * @author 志军 */ public class ContrastUtilExample extends AbstractContrastUtil { + + /** + * 处理入参为null的情况:两者都为null返回null,否则返回非null一方的结果 + */ @Override ContrastObject nullProcessor(T origin, T target) { if (origin == null && target == null) { @@ -21,6 +29,9 @@ public class ContrastUtilExample extends AbstractContrastUtil { return setContrastObject(origin, null, origin, null); } + /** + * 处理基本类型和包装类型的对比 + */ @Override ContrastObject originProcessor(T origin, T target) { if (origin == target) { diff --git a/src/main/java/bean/ContrastObject.java b/src/main/java/bean/ContrastObject.java index 07853bf..2af62c0 100644 --- a/src/main/java/bean/ContrastObject.java +++ b/src/main/java/bean/ContrastObject.java @@ -81,10 +81,10 @@ public class ContrastObject { return false; } ContrastObject that = (ContrastObject) o; - return getOrigin().equals(that.getOrigin()) && - getTarget().equals(that.getTarget()) && - getOriginChange().equals(that.getOriginChange()) && - getTargetChange().equals(that.getTargetChange()); + return Objects.equals(getOrigin(), that.getOrigin()) && + Objects.equals(getTarget(), that.getTarget()) && + Objects.equals(getOriginChange(), that.getOriginChange()) && + Objects.equals(getTargetChange(), that.getTargetChange()); } @Override diff --git a/src/main/java/constant/Constant.java b/src/main/java/constant/Constant.java index d4cbd9a..e73a600 100644 --- a/src/main/java/constant/Constant.java +++ b/src/main/java/constant/Constant.java @@ -7,5 +7,4 @@ public class Constant { public static final String GET = "get"; public static final String SET = "set"; - public static final String WELL_NUMBER = "#"; } diff --git a/src/main/java/handler/CollectionHandler.java b/src/main/java/handler/CollectionHandler.java index 4c5dce9..30fd94b 100644 --- a/src/main/java/handler/CollectionHandler.java +++ b/src/main/java/handler/CollectionHandler.java @@ -43,10 +43,10 @@ public class CollectionHandler { return false; } - Class returnValue = originValue == null ? targetValue.getClass() : originValue.getClass(); + Class clazz = originValue == null ? targetValue.getClass() : originValue.getClass(); - Object originCollection = CollectionUtil.buildDiffCollection(originValue, targetValue, returnValue); - Object changeCollection = CollectionUtil.buildDiffCollection(targetValue, originValue, returnValue); + Object originCollection = CollectionUtil.buildDiffCollection(originValue, targetValue, clazz); + Object changeCollection = CollectionUtil.buildDiffCollection(targetValue, originValue, clazz); changed = CollectionUtils.isNotEmpty((Collection) originCollection) || CollectionUtils.isNotEmpty((Collection) changeCollection); diff --git a/src/main/java/handler/MapHandler.java b/src/main/java/handler/MapHandler.java index a098904..140be93 100644 --- a/src/main/java/handler/MapHandler.java +++ b/src/main/java/handler/MapHandler.java @@ -6,7 +6,7 @@ package handler; import com.esotericsoftware.reflectasm.MethodAccess; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import util.MapUtil; import util.SetUtil; @@ -25,8 +25,7 @@ public class MapHandler { public static boolean putValue(Object originChange, Object targetChange, Object originValue, Object targetValue, MethodAccess methodAccess, String setName) throws IllegalAccessException, InstantiationException { - //这边也只处理只有一个map可能为空的情况,全部为空在前面的方法做了处理 - if (SelfHandler.isOneNull(originChange, originChange, targetValue, originValue, methodAccess, setName)) { + if (SelfHandler.isOneNull(originChange, targetChange, originValue, targetValue, methodAccess, setName)) { return true; } //参数前置 @@ -52,25 +51,31 @@ public class MapHandler { //得到不一致的key的集合. List originList = SetUtil.findUnLikeValue(originEntry, targetEntry); - List changeList = SetUtil.findUnLikeValue(originEntry, targetEntry); + List changeList = SetUtil.findUnLikeValue(targetEntry, originEntry); //计算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); + methodAccess.invoke(originChange, setName, originMap); + methodAccess.invoke(targetChange, setName, targetMap); return changed; } + /** + * 比较Map中同一个key对应的两个value,如果不相等则将差异设入对应的Map + */ private static boolean putValue(Object value, Object tValue, Map originMap, Map targetMap, Object key) { - if (value.equals(tValue)) { + if (value == null && tValue == null) { return false; } - originMap.put(key, value); - targetMap.put(key, tValue); - return true; + if (value == null || tValue == null || !value.equals(tValue)) { + originMap.put(key, value); + targetMap.put(key, tValue); + return true; + } + return false; } } diff --git a/src/main/java/handler/OriginHandler.java b/src/main/java/handler/OriginHandler.java index 5654c49..5b72eae 100644 --- a/src/main/java/handler/OriginHandler.java +++ b/src/main/java/handler/OriginHandler.java @@ -17,31 +17,27 @@ 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(); + Class clazz = 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类型 + if (clazz.getClassLoader() == null) { return contractValue(originChange, targetChange, originValue, targetValue, methodAccess, setName); } + return null; } 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 { + if (originValue == null || targetValue == null) { methodAccess.invoke(originChange, setName, originValue); methodAccess.invoke(targetChange, setName, targetValue); return true; } + if (originValue.equals(targetValue)) { + return false; + } + methodAccess.invoke(originChange, setName, originValue); + methodAccess.invoke(targetChange, setName, targetValue); + return true; } } diff --git a/src/main/java/handler/SelfHandler.java b/src/main/java/handler/SelfHandler.java index 63259b4..65e8ec4 100644 --- a/src/main/java/handler/SelfHandler.java +++ b/src/main/java/handler/SelfHandler.java @@ -28,6 +28,17 @@ public class SelfHandler { return false; } + /** + * 判断两个值是否其中一个为null(另一个不为null),如果是则将非null值设入对应的change对象 + * + * @param originChange 原始变化对象 + * @param targetChange 修改后变化对象 + * @param originValue 原始值 + * @param targetValue 修改后的值 + * @param methodAccess reflectASM方法访问器 + * @param setName set方法名 + * @return 是否其中一个为null + */ static boolean isOneNull(Object originChange, Object targetChange, Object originValue, Object targetValue, MethodAccess methodAccess, String setName) { if (originValue == null) { methodAccess.invoke(targetChange, setName, targetValue); diff --git a/src/main/java/util/CheckUtil.java b/src/main/java/util/CheckUtil.java index 8893da1..a7924f5 100644 --- a/src/main/java/util/CheckUtil.java +++ b/src/main/java/util/CheckUtil.java @@ -8,7 +8,8 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.List; -import java.util.Vector; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import static constant.Constant.GET; import static constant.Constant.SET; @@ -19,7 +20,7 @@ import static constant.Constant.SET; */ public class CheckUtil { - private static Vector vector = new Vector<>(); + private static Set checkedClassSet = ConcurrentHashMap.newKeySet(); /** @@ -29,7 +30,7 @@ public class CheckUtil { * @param zClass Class类 */ public static void checkGetAndSetMethod(Class zClass) { - if (vector.contains(zClass.getName())) { + if (checkedClassSet.contains(zClass.getName())) { return; } List fieldList = ClassUtil.getAllFieldWithSuper(zClass); @@ -48,9 +49,12 @@ public class CheckUtil { throw new NoSetMethodException(); } } - vector.add(zClass.getName()); + checkedClassSet.add(zClass.getName()); } + /** + * 校验setter方法的参数类型是否与字段类型匹配 + */ private static boolean setMethodParamCheck(Field field, String setMethodName, List methodList) { for (Method method : methodList) { if (method.getName().equals(setMethodName) && method.getParameterTypes()[0] == field.getType()) { @@ -60,6 +64,9 @@ public class CheckUtil { return false; } + /** + * 提取方法名列表 + */ private static List getMethodNameList(List methodList) { List methodNameList = Lists.newArrayList(); for (Method method : methodList) { diff --git a/src/main/java/util/ClassUtil.java b/src/main/java/util/ClassUtil.java index fe5c271..ffe9907 100644 --- a/src/main/java/util/ClassUtil.java +++ b/src/main/java/util/ClassUtil.java @@ -1,6 +1,5 @@ package util; -import com.esotericsoftware.reflectasm.MethodAccess; import com.google.common.collect.Lists; import java.lang.reflect.Field; @@ -9,21 +8,12 @@ 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 - * 其实你不用担心类似 ##fieldCache.get(zClass.getName())这样的代码在一个方法里面写了很多遍会处理多次 - *

可以了解一下##方法内联 */ public class ClassUtil { - /** - * 类名#方法 - 方法索引 缓存 - */ - private static ConcurrentHashMap cache = new ConcurrentHashMap(); - /** * 类 - Field数组 缓存 */ @@ -35,46 +25,6 @@ public class ClassUtil { private static ConcurrentHashMap> methodCache = new ConcurrentHashMap>(); - /** - * 获取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 getAllFieldWithSuper(Class zClass) { if (fieldCache.get(zClass.getName()) != null) { return fieldCache.get(zClass.getName()); diff --git a/src/main/java/util/CollectionUtil.java b/src/main/java/util/CollectionUtil.java index f302c27..598b175 100644 --- a/src/main/java/util/CollectionUtil.java +++ b/src/main/java/util/CollectionUtil.java @@ -16,9 +16,8 @@ public class CollectionUtil { @SuppressWarnings("unchecked") - public static Collection buildDiffCollection(Object originValue, Object targetValue, Class returnValue) throws IllegalAccessException, + public static Collection buildDiffCollection(Object originValue, Object targetValue, Class collectionClass) throws IllegalAccessException, InstantiationException { - //数组为空的判断这边需要考虑的只有一个为空另一个不为空,因为在外面已经进行了其他两种情况的处理了 if (null == targetValue) { return (Collection) originValue; } @@ -26,7 +25,7 @@ public class CollectionUtil { return null; } - Object collection = returnValue.newInstance(); + Object collection = collectionClass.newInstance(); for (Object value : (Collection) originValue) { if (((Collection) targetValue).contains(value)) { diff --git a/src/main/java/util/MapUtil.java b/src/main/java/util/MapUtil.java index c322809..10bf406 100644 --- a/src/main/java/util/MapUtil.java +++ b/src/main/java/util/MapUtil.java @@ -5,7 +5,7 @@ package util; -import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections4.CollectionUtils; import java.util.List; import java.util.Map; diff --git a/src/main/test/ContractTest.java b/src/main/test/ContractTest.java index b255cd6..c9b9417 100644 --- a/src/main/test/ContractTest.java +++ b/src/main/test/ContractTest.java @@ -9,13 +9,13 @@ public class ContractTest { public static void main(String[] args) throws InstantiationException, IllegalAccessException { -// testSimple(); -// testObject(); -// testSimpleMap(); -// testSimpleList(); -// testListList(); -// testListObject(); -// testListObjectRewriteEqual(); + testSimple(); + testObject(); + testSimpleMap(); + testSimpleList(); + testListList(); + testListObject(); + testListObjectRewriteEqual(); } private static void testObject() throws InstantiationException, IllegalAccessException {