├── .gitignore
├── README.md
├── core
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── github
│ │ └── spsl
│ │ └── objectdiff
│ │ └── core
│ │ ├── AbstractDiffer.java
│ │ ├── Context.java
│ │ ├── DiffNode.java
│ │ ├── Differ.java
│ │ ├── DifferClassFactory.java
│ │ ├── DifferClassGenerator.java
│ │ ├── DifferClassWrapper.java
│ │ ├── DifferFactory.java
│ │ ├── Filter.java
│ │ ├── GeneratorDiffException.java
│ │ ├── JavassistDifferClassGenerator.java
│ │ ├── ObjectEqualsDiffer.java
│ │ ├── State.java
│ │ ├── Tuple2.java
│ │ └── Visitor.java
│ └── test
│ └── java
│ └── com
│ └── github
│ └── spsl
│ └── objectdiff
│ └── core
│ ├── BaseDiffCompare.java
│ └── Foo.java
├── example
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── github
│ └── spsl
│ └── objectdiff
│ └── example
│ ├── Main.java
│ ├── model
│ ├── School.java
│ └── Student.java
│ └── typecircular
│ ├── Bar.java
│ ├── Foo.java
│ └── Test.java
└── pom.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | *.iml
3 | target/
4 | */target/
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ObjectDiff
2 |
3 | ---
4 | 对比两个java对象的差异,支持原生类型,自定义类型,以及集合类型,采用Javassist动态字节码增强技术,动态生成比较类。
5 |
6 |
7 | ## 目前已经实现的功能
8 | 1. 基础类型比较
9 | 2. 属性自定义类型递归比较
10 | 3. 集合类型和数组类型比较
11 | 4. 生成的Differ类循环依赖问题
12 | 5. 对象循环依赖
13 |
14 | ## TODOs
15 | 1. 属性过滤
16 | 2. 属性黑名单和白名单过滤模式
17 | 3 自定义集合迭代和比较策略
18 |
--------------------------------------------------------------------------------
/core/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | object-diff
7 | com.github.spsl
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | object-diff-core
13 |
14 |
15 | 8
16 | 8
17 |
18 |
19 |
20 |
21 | org.javassist
22 | javassist
23 |
24 |
25 | junit
26 | junit
27 | test
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/core/src/main/java/com/github/spsl/objectdiff/core/AbstractDiffer.java:
--------------------------------------------------------------------------------
1 | package com.github.spsl.objectdiff.core;
2 |
3 | import java.util.*;
4 | import java.util.concurrent.ConcurrentHashMap;
5 | import java.util.concurrent.atomic.AtomicReference;
6 |
7 | public abstract class AbstractDiffer implements Differ {
8 |
9 | private static final ThreadLocal diffContext = new ThreadLocal<>();
10 |
11 | private final Map> differMap = new ConcurrentHashMap<>();
12 |
13 | protected boolean existDiffer(String differTypeName) {
14 | return differMap.containsKey(differTypeName);
15 | }
16 |
17 | protected boolean checkIsTracked(Object obj) {
18 | if (obj == null) {
19 | return false;
20 | }
21 | return diffContext.get().getTracker().contains(obj);
22 | }
23 |
24 | @Override
25 | public Optional diff(Object from, Object to) {
26 | return diff(from, to, null);
27 | }
28 | @Override
29 | public Optional diff(Object from, Object to, Filter filter) {
30 | try {
31 | diffContext.set(new Context());
32 | return diff(null, "", from, to, filter);
33 | } finally {
34 | diffContext.remove();
35 | }
36 | }
37 |
38 | protected Optional diff(DiffNode parentNode, String propertyName, Object origin, Object target, Filter filter) {
39 | if (Objects.equals(origin, target)) {
40 | return Optional.empty();
41 | }
42 |
43 | if (filter != null && filter.excludeProperties() != null && !filter.excludeProperties().isEmpty()) {
44 | diffContext.get().getExcludeProperties().addAll(filter.excludeProperties());
45 | }
46 | return doDiff(parentNode, propertyName, origin, target);
47 | }
48 |
49 | protected Set excludeProperties() {
50 | return diffContext.get().getExcludeProperties();
51 | }
52 |
53 | protected abstract Optional doDiff(DiffNode parentNode, String propertyName, Object origin, Object target);
54 |
55 | protected DiffNode initDiffNode(DiffNode parentNode, String propertyName, Object origin, Object target) {
56 | DiffNode diffNode = new DiffNode();
57 |
58 | diffNode.setParentNode(parentNode);
59 | diffNode.setProperty(propertyName);
60 | diffNode.setOriginValue(origin);
61 | diffNode.setTargetValue(target);
62 | if (parentNode == null) {
63 | diffNode.setFullPath("");
64 | } else {
65 | diffNode.setFullPath(calculateFullPath(parentNode, propertyName));
66 | }
67 | return diffNode;
68 | }
69 |
70 | private String calculateFullPath(DiffNode parentNode, String propertyName) {
71 | if (parentNode == null || parentNode.getFullPath() == null || parentNode.getFullPath().trim() == "") {
72 | return "/" + propertyName;
73 | }
74 | return parentNode.getFullPath() + "." + propertyName;
75 | }
76 |
77 | public void setDiffer(String differName, AtomicReference differReference) {
78 | differMap.put(differName, differReference);
79 | }
80 |
81 | protected Optional primitiveDiff(DiffNode parentNode, String propertyName, Integer from, Integer to) {
82 | return immutableObjectDiff(parentNode, propertyName, from, to);
83 | }
84 |
85 | protected Optional primitiveDiff(DiffNode parentNode, String propertyName, Short from, Short to) {
86 | return immutableObjectDiff(parentNode, propertyName, from, to);
87 | }
88 |
89 | protected Optional primitiveDiff(DiffNode parentNode, String propertyName, Long from, Long to) {
90 | return immutableObjectDiff(parentNode, propertyName, from, to);
91 | }
92 |
93 | protected Optional primitiveDiff(DiffNode parentNode, String propertyName, Double from, Double to) {
94 | return immutableObjectDiff(parentNode, propertyName, from, to);
95 | }
96 |
97 | protected Optional primitiveDiff(DiffNode parentNode, String propertyName, Float from, Float to) {
98 | return immutableObjectDiff(parentNode, propertyName, from, to);
99 | }
100 |
101 | protected Optional primitiveDiff(DiffNode parentNode, String propertyName, Byte from, Byte to) {
102 | return immutableObjectDiff(parentNode, propertyName, from, to);
103 | }
104 |
105 | protected Optional primitiveDiff(DiffNode parentNode, String propertyName, String from, String to) {
106 | return immutableObjectDiff(parentNode, propertyName, from, to);
107 | }
108 |
109 | protected Optional primitiveDiff(DiffNode parentNode, String propertyName, Boolean from, Boolean to) {
110 | return immutableObjectDiff(parentNode, propertyName, from, to);
111 | }
112 |
113 | protected Optional primitiveDiff(DiffNode parentNode, String propertyName, Character from, Character to) {
114 | return immutableObjectDiff(parentNode, propertyName, from, to);
115 | }
116 |
117 | protected Optional primitiveDiff(DiffNode parentNode, String propertyName, byte from, byte to) {
118 | return immutableObjectDiff(parentNode, propertyName, from, to);
119 | }
120 |
121 | protected Optional primitiveDiff(DiffNode parentNode, String propertyName, char from, char to) {
122 | return immutableObjectDiff(parentNode, propertyName, from, to);
123 | }
124 |
125 | protected Optional primitiveDiff(DiffNode parentNode, String propertyName, short from, short to) {
126 | return immutableObjectDiff(parentNode, propertyName, from, to);
127 | }
128 |
129 | protected Optional primitiveDiff(DiffNode parentNode, String propertyName, int from, int to) {
130 | return immutableObjectDiff(parentNode, propertyName, from, to);
131 | }
132 |
133 | protected Optional primitiveDiff(DiffNode parentNode, String propertyName, float from, float to) {
134 | return immutableObjectDiff(parentNode, propertyName, from, to);
135 | }
136 |
137 | protected Optional primitiveDiff(DiffNode parentNode, String propertyName, long from, long to) {
138 | return immutableObjectDiff(parentNode, propertyName, from, to);
139 | }
140 |
141 | protected Optional primitiveDiff(DiffNode parentNode, String propertyName, double from, double to) {
142 | return immutableObjectDiff(parentNode, propertyName, from, to);
143 | }
144 |
145 | protected Optional primitiveDiff(DiffNode parentNode, String propertyName, boolean from, boolean to) {
146 | return immutableObjectDiff(parentNode, propertyName, from, to);
147 | }
148 |
149 | protected Optional immutableObjectDiff(DiffNode parentNode, String propertyName, Object from, Object to) {
150 | if (Objects.equals(from,to)) {
151 | return Optional.empty();
152 | }
153 | DiffNode node = initDiffNode(parentNode, propertyName, from, to);
154 | if (from == null) {
155 | node.setState(State.ADDED);
156 | } else if (to == null) {
157 | node.setState(State.DELETED);
158 | } else {
159 | node.setState(State.CHANGED);
160 | }
161 | return Optional.of(node);
162 | }
163 |
164 | protected boolean exclude(DiffNode parentNode, String propertyName) {
165 | if (parentNode != null) {
166 | return false;
167 | }
168 | return diffContext.get().getExcludeProperties().contains(propertyName);
169 | }
170 |
171 | protected Optional customObjectDiff(String customTypeName, DiffNode parentNode, String propertyName, Object from, Object to) {
172 | AtomicReference differReference = differMap.get(customTypeName);
173 | if (Objects.isNull(differReference) || differReference.get() == null) {
174 | return immutableObjectDiff(parentNode, propertyName, from, to);
175 | }
176 | Tuple2