对于两个实体类属性值的合并,java实现
由于这篇文章的浏览量更多, 一直也没来及更新,6年前写的文章。内容也一直没升级更新。
对象拷贝,在实际的业务场景中非常场景,刚好今天有时间,也来更新下这篇文章的内容。
主要分为两部分:第三方组件实现 + 反射的方式
1、第三方组件 reflectasm实现(强烈推荐)
POM 依赖:
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>reflectasm</artifactId>
<version>1.11.3</version>
</dependency>
废话不多说,直接上代码 BeanCopyUtil.class
import com.esotericsoftware.reflectasm.MethodAccess;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class BeanCopyUtil {
/**
* 方法缓存
*/
private static Map<Class, MethodAccess> methodMap = new ConcurrentHashMap<>();
/**
* 方法 index 缓存
*/
private static Map<String, Integer> methodIndexMap = new ConcurrentHashMap<>();
/**
* 字段缓存
*/
private static Map<Class, List<String>> fieldMap = new ConcurrentHashMap<>();
/**
* 拷贝对象
*
* @param source
* @param target
*/
public static void copyProperties(Object source, Object target) {
copyProperties(source, target, true);
}
public static void copyProperties(Object source, Object target, boolean ignoreNull) {
MethodAccess targetMethodAccess = methodMap.get(target.getClass());
if (targetMethodAccess == null) {
targetMethodAccess = cache(target);
}
MethodAccess sourceMethodAccess = methodMap.get(source.getClass());
if (sourceMethodAccess == null) {
sourceMethodAccess = cache(source);
}
List<String> fieldList = fieldMap.get(source.getClass());
for (String field : fieldList) {
String setKey = target.getClass().getName() + "." + "set" + field;
Integer setIndex = methodIndexMap.get(setKey);
if (setIndex != null) {
String getKey = source.getClass().getName() + "." + "get" + field;
int getIndex = methodIndexMap.get(getKey);
// 参数一需要反射的对象
// 参数二class.getDeclaredMethods 对应方法的index
// 参数对三象集合
Object invokeValue = sourceMethodAccess.invoke(source, getIndex);
if (ignoreNull && invokeValue == null) {
continue;
}
targetMethodAccess.invoke(target, setIndex.intValue(), invokeValue);
}
}
}
// 单例模式
private static MethodAccess cache(Object orgi) {
synchronized (orgi.getClass()) {
MethodAccess methodAccess = MethodAccess.get(orgi.getClass());
List<Field> fields = getAllFields(orgi);
List<String> fieldList = new ArrayList<>(fields.size());
for (Field field : fields) {
if (Modifier.isPrivate(field.getModifiers())
&& !Modifier.isStatic(field.getModifiers())) { // 是否是私有的,是否是静态的
// 非公共私有变量
String fieldName = StringUtils.capitalize(field.getName()); // 获取属性名称
int getIndex = methodAccess.getIndex("get" + fieldName); // 获取get方法的下标
int setIndex = methodAccess.getIndex("set" + fieldName); // 获取set方法的下标
methodIndexMap.put(orgi.getClass().getName() + "." + "get"
+ fieldName, getIndex); // 将类名get方法名,方法下标注册到map中
methodIndexMap.put(orgi.getClass().getName() + "." + "set"
+ fieldName, setIndex); // 将类名set方法名,方法下标注册到map中
fieldList.add(fieldName); // 将属性名称放入集合里
}
}
fieldMap.put(orgi.getClass(), fieldList); // 将类名,属性名称注册到map中
methodMap.put(orgi.getClass(), methodAccess);
return methodAccess;
}
}
/**
* 获取所有属性,包括 继承方式
*
* @param orgi
* @return
*/
private static List<Field> getAllFields(Object orgi) {
List<Field> fields = Lists.newArrayList();
//当前类自己的属性
Field[] declaredFields4Local = orgi.getClass().getDeclaredFields();
if (declaredFields4Local != null) {
fields.addAll(Lists.newArrayList(declaredFields4Local));
}
/**
* 获取继承类的属性
*/
Class<?> superClass = orgi.getClass();
do {
superClass = superClass.getSuperclass();
if (superClass == Object.class) {
break;
}
if (superClass.getDeclaredFields() != null) {
fields.addAll(Lists.newArrayList(superClass.getDeclaredFields()));
}
} while (superClass != Object.class);
return fields;
}
}
参考:https://blog.csdn.net/liaodehong/article/details/50379351
2、手写反射 的方式
对于实现,主要是使用 java 的反射机制。
首先需要构造一个实体类(TestModel.java):
package test; public class TestModel { private String prop1; private String prop2; private String prop3; public String getProp1() { return prop1; } public void setProp1(String prop1) { this.prop1 = prop1; } public String getProp2() { return prop2; } public void setProp2(String prop2) { this.prop2 = prop2; } public String getProp3() { return prop3; } public void setProp3(String prop3) { this.prop3 = prop3; } //用于输出字符串 @Override public String toString() { return "prop1:"+prop1+"\nprop2:"+prop2+"\nprop3:"+prop3; } }
然后紧接着创建一个合并相同实体的功能类():
package test; import java.lang.reflect.Field; public class CombineBeans { /** * 该方法是用于相同对象不同属性值的合并,如果两个相同对象中同一属性都有值,那么sourceBean中的值会覆盖tagetBean重点的值 * @param sourceBean 被提取的对象bean * @param targetBean 用于合并的对象bean * @return targetBean,合并后的对象 */ private TestModel combineSydwCore(TestModel sourceBean,TestModel targetBean){ Class sourceBeanClass = sourceBean.getClass(); Class targetBeanClass = targetBean.getClass(); Field[] sourceFields = sourceBeanClass.getDeclaredFields(); Field[] targetFields = targetBeanClass.getDeclaredFields(); for(int i=0; i<sourceFields.length; i++){ Field sourceField = sourceFields[i];
if(Modifier.isStatic(sourceField.getModifiers())){
continue;
} Field targetField = targetFields[i];
if(Modifier.isStatic(targetField.getModifiers())){
continue;
} sourceField.setAccessible(true); targetField.setAccessible(true); try { if( !(sourceField.get(sourceBean) == null) && !"serialVersionUID".equals(sourceField.getName().toString())){ targetField.set(targetBean,sourceField.get(sourceBean)); } } catch (IllegalArgumentException | IllegalAccessException e) { e.printStackTrace(); } } return targetBean; } //测试 combineBeans方法 public static void main(String[] args) { TestModel sourceModel = new TestModel(); // 第一个对象 TestModel targetModel = new TestModel(); // 第二个model对象 sourceModel.setProp1("1"); sourceModel.setProp2("1"); targetModel.setProp2("2"); targetModel.setProp3("2"); CombineBeans test = new CombineBeans(); test.combineSydwCore(sourceModel, targetModel); System.out.println(targetModel.toString()); } }
输出的结果如下:

根据控制台中打印结果发现:
原来的targetModel属性值:
prop1:null prop2:2 prop3:2
合并之后的targetModel属性值:
prop1:1 prop2:1 prop3:2
targetModel中的 prop1 属性被 sourceModel 中的 prop1 属性填充;prop2 属性被 prop1 覆盖。
在后续使用中发现,应该排除 静态域,具体判断方法参考:https://blog.csdn.net/zhou8622/article/details/44038699
到此,over !!! ^_^
Read the fucking manual and source code

浙公网安备 33010602011771号