对于两个实体类属性值的合并,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