`

基于反射进行对象属性的拷贝

阅读更多

    在实际的工作中,有时可能存在2个java bean属性之间的拷贝,而如果使用bean 之间setter方法进行设置,那么将会存在大量的冗余的代码,因此可以考虑使用反射来进行属性的拷贝操作。

大致思路如下:

1、从class文件中,获取到所有的public类型的方法

2、获取到所有的getter方法和setter方法,getter方法的获取需要考虑到boolean类型这个比较特殊的类型的获取。

3、进行属性的拷贝的时候,需要考虑到源对象中的属性值为null,是否应该拷贝到目标对象中

4、一个class文件中的getter和setter方法一般都是不可变的,因此需要进行缓存起来,避免每次都进行获取。

一、编写BeanUtils工具类,实现拷贝

/**
 * bean 之间的属性的复制.
 * 
 * @描述
 * @作者 huan
 * @时间 2017年11月4日 - 上午11:25:26
 */
public final class BeanUtils {

	private static final ConcurrentHashMap<Class<?>, List<Method>> CACHE_CLASS_GET_METHOD = new ConcurrentHashMap<>();
	private static final ConcurrentHashMap<Class<?>, List<Method>> CACHE_CLASS_SET_METHOD = new ConcurrentHashMap<>();

	private BeanUtils() {
	}

	/**
	 * 将src java bean的属性拷贝到 tar java bean的属性中,默认不拷贝src对象中的空对象的值.
	 *
	 * @param src
	 *            源对象
	 * @param tar
	 *            目标对象
	 */
	public static void copyProperties(Object src, Object tar) {
		copyProperties(src, tar, true);
	}

	/**
	 * 将src java bean的属性拷贝到 tar java bean的属性中
	 *
	 * @param src
	 *            源对象
	 * @param tar
	 *            目标对象
	 * @param skipEmpty
	 *            如果src中属性的值时null,是否跳过这个拷贝, true:跳过 false:不跳过
	 */
	public static void copyProperties(Object src, Object tar, boolean skipEmpty) {
		List<Method> getterMethods = getGetMethod(src.getClass());
		List<Method> setterMethods = getSetMethod(tar.getClass());
		try {
			for (Method getMethod : getterMethods) {
				Object value = getMethod.invoke(src);
				if (skipEmpty && value == null) {
					continue;
				}
				Method method = setterMethods.stream().filter(m -> Objects.equals(m.getName(), getInvokedSetterMethodName(getMethod.getName()))).findFirst().orElse(null);
				if (null != method) {
					method.invoke(tar, value);
				}
			}
		} catch (IllegalAccessException | InvocationTargetException e) {
			throw new RuntimeException("bean复制属性过程中产生异常", e);
		}
	}

	/**
	 * 根据getter的方法名拿到setter的方法名
	 *
	 * @param getterMethodName
	 *            getter的方法名
	 * @return setter的方法名
	 */
	public static String getInvokedSetterMethodName(String getterMethodName) {
		if (getterMethodName.startsWith("get")) {
			return "set" + getterMethodName.substring(3);
		} else {
			return "set" + getterMethodName.substring(2);
		}
	}

	/**
	 * 获取clazz中的所有public getter 方法
	 *
	 * @param clazz
	 *            需要获取getter方法的类
	 * @return public getter方法
	 */
	public static List<Method> getGetMethod(Class<?> clazz) {
		Class<?> lockClazz = clazz;
		if (!CACHE_CLASS_GET_METHOD.containsKey(clazz)) {
			synchronized (lockClazz) {
				if (!CACHE_CLASS_GET_METHOD.containsKey(clazz)) {
					CACHE_CLASS_GET_METHOD.put(clazz, Arrays.stream(getMethods(clazz)).filter(BeanUtils::isGetterMethod).filter(m -> !Objects.equals(m.getName(), "getClass")).collect(Collectors.toList()));
				}
			}
		}
		return CACHE_CLASS_GET_METHOD.get(clazz);
	}

	/**
	 * 获取clazz中的所有public setter 方法
	 *
	 * @param clazz
	 *            需要获取setter方法的类
	 * @return public setter方法
	 */
	public static List<Method> getSetMethod(Class<?> clazz) {
		Class<?> lockClazz = clazz;
		if (!CACHE_CLASS_SET_METHOD.containsKey(clazz)) {
			synchronized (lockClazz) {
				if (!CACHE_CLASS_SET_METHOD.containsKey(clazz)) {
					CACHE_CLASS_SET_METHOD.put(clazz, Arrays.stream(getMethods(clazz)).filter(BeanUtils::isSetterMethod).collect(Collectors.toList()));
				}
			}
		}
		return CACHE_CLASS_SET_METHOD.get(clazz);
	}

	/**
	 * 获取一个类中中的所有的方法
	 *
	 * @param clazz
	 * @return
	 */
	public static Method[] getMethods(Class<?> clazz) {
		return clazz.getMethods();
	}

	/**
	 * 判断一个方式是否是getter方法
	 *
	 * @param method
	 *            需要判断的方法
	 * @return true:是getter方法 false:不是getter方法
	 */
	public static boolean isGetterMethod(Method method) {
		if (method.getName().startsWith("get") && method.getParameterCount() == 0 && method.getReturnType() != Void.class) {
			return true;
		} else if (method.getName().startsWith("is") && method.getParameterCount() == 0 && (method.getReturnType().equals(boolean.class) || method.getReturnType().equals(Boolean.class))) {
			return true;
		}
		return false;
	}

	/**
	 * 判断是否是setter方法
	 *
	 * @param method
	 *            需要判断的方法
	 * @return true是setter方法 false不是setter方法
	 */
	public static boolean isSetterMethod(Method method) {
		boolean isSetter = false;
		if (method.getName().startsWith("set") && method.getParameterCount() == 1 && method.getReturnType() != Void.class) {
			isSetter = true;
		}
		return isSetter;
	}
}

 二、测试

   1、编写一个实体类 SysResource

/**
 * 资源实体类
 * 
 * @描述
 * @作者 huan
 * @时间 2017年11月4日 - 上午11:26:46
 */
@Data
public class SysResource {
	private String id;
	private String pid;
	private String name;
	private String url;
	private Integer type;
	private Date createTime;
}

   2、编写测试代码

 

 

  • 大小: 84.8 KB
分享到:
评论

相关推荐

    C#基于表达式(Expression)实现对象深拷贝

    在C#编程中,对象的深拷贝是一个重要的概念,特别是在处理复杂的数据结构时,我们需要确保复制的对象与原对象在内存中是独立的,修改其中一个不会影响另一个。本主题将详细探讨如何使用C#的表达式(Expression)来...

    BeanConvertor工具类Java开发Bean转换拷贝工具类

    如何使用Java反射机制进行对象属性拷贝。 如何利用Jackson库实现复杂类型的对象转换。 不同转换方式的适用场景及其优劣。 批量对象转换的方法和实践。 阅读建议:在学习过程中,建议读者结合实际项目需求,尝试使用...

    对象拷贝-优雅的解决方案Mapstruct.docx

    传统的对象属性拷贝方式如 `BeanUtils.copyProperties` 虽然简单易用,但存在一些问题: 1. **性能问题**:基于反射的拷贝方式相对较慢。 2. **灵活性不足**:对于同名字段无法进行特殊处理,这可能导致某些不希望...

    beanUtils , CopyProperty

    总的来说,Apache BeanUtils库的`copyProperties`方法是Java开发中一个实用的工具,它可以显著提高代码的可读性和简洁性,尤其在处理JavaBean对象属性拷贝时。然而,理解它的内在工作原理和潜在限制同样非常重要,...

    自己开发的一些例子反射

    5. 实现通用操作:反射可以用来实现一些通用的功能,例如序列化/反序列化、属性拷贝等。 标签“例子程序”暗示这个压缩包可能包含以下示例: 1. 基本反射操作:展示如何获取类信息、构造器、方法和字段。 2. 动态...

    common(bean_之音属性复制).zip

    标题中的"common(bean_之音属性复制).zip"提到了一个关于Java Bean属性复制的主题,这通常指的是对象间的属性拷贝或者映射过程。在Java开发中,特别是Spring框架中,Bean的属性复制是一个常见操作,它有助于减少...

    C#两个相同属性的类赋值方法

    5. 深拷贝与浅拷贝:这种方法仅进行浅拷贝,即引用类型属性的值只是指向了同一个对象。如果需要深拷贝(即复制对象的所有嵌套对象),则需要更复杂的逻辑。 6. 自定义逻辑:如果属性赋值需要特定的逻辑,例如验证、...

    BeanUtils.jar

    在Java编程世界中,BeanUtils.jar是一个极为重要的工具包,尤其在处理对象属性操作时,它的存在极大地简化了开发工作。BeanUtils是Apache Commons项目的一部分,提供了丰富的功能,用于方便地操作JavaBeans。本文将...

    302.300.JAVA基础教程_面向对象(中)-Object类综合练习(302).rar

    在实际开发中,我们常常需要根据对象的属性来比较,所以会重写`equals()`方法,实现基于对象内容的比较。 3. `hashCode()`方法:`hashCode()`方法返回对象的哈希码,用于哈希表(如`HashMap`和`HashSet`)的操作。`...

    commons相关jar

    - **核心功能**:属性拷贝、属性访问(通过反射机制)、转换器(Converter)支持,用于在不同数据类型之间转换属性值。 - **使用示例**:`BeanUtils.copyProperties(sourceBean, targetBean)`可以将源对象的所有...

    Bean复制的几种框架性能比较(Apache BeanUtils、PropertyUtils,Spring BeanUtils,Cglib BeanCopier

    它使用反射机制实现,通过getter和setter方法进行属性的读写。其优点在于简单易用,但缺点是在大量复制操作时性能较低,因为每次复制都需要进行反射调用。 PropertyUtils是Apache Commons BeanUtils的扩展,它增强...

    BeanUtil框架完整包

    BeanUtil框架是一个在Java开发中常用的工具库,主要用于对象属性的获取、设置、拷贝以及类型转换等操作。它的设计目标是简化Java Bean的操作,提高代码的可读性和可维护性。这个“BeanUtil框架完整包”包含了...

    apache BeanUtils

    在Java开发中,BeanUtils库极大地提高了开发效率,尤其是在处理对象属性的设置、获取以及复制等方面。以下是对Apache BeanUtils核心功能的详细介绍: 1. 属性自动封装与解封: BeanUtils库的主要功能之一就是自动将...

    Java开发手册(嵩山版)灵魂15问.pdf

    因此,开发手册建议避免使用Apache BeanUtils,转而采用更安全、性能更好的工具进行对象属性的复制。 至于日期格式化时使用'y'而不是'Y',这是因为'y'代表年的历元年(例如公历年),而'Y'代表周年的年份(基于星期...

    commons-beanutils.jar.zip

    1. **属性拷贝**:BeanUtils.copyProperties()方法可以实现两个JavaBean对象之间的属性值拷贝,极大地减少了代码量。 2. **动态属性访问**:通过BeanUtils.getProperty()和BeanUtils.setProperty(),我们可以动态地...

    Dozer 使用总结,也许对你有帮助

    对于源码分析,Dozer 的实现基于反射和泛型,这使得它能够处理各种类型的对象。它的性能在大多数情况下是可接受的,但如果映射操作非常频繁,可能需要考虑优化,例如通过缓存映射结果来减少不必要的计算。 在选择...

    iOS开发Runtime的用法

    KVC是Runtime提供的一种间接访问对象属性的方式,无需知道属性的setter和getter。KVO则允许我们监听某个属性的变化,当属性值改变时,会自动触发观察者的方法。两者都依赖于Runtime的底层实现。 6. **Category和...

    BeanUtils详细讲解.pdf

    在实际应用中,BeanUtils可以极大地简化Java对象属性的访问和修改过程,尤其是在需要处理大量数据或进行复杂业务逻辑时。它提供了一种方便、统一的方式来操作JavaBean,使得开发者可以更专注于业务逻辑的实现,而非...

    commons-beanutils-1.9.2-src

    Apache Commons BeanUtils 是一个流行的Java工具库,主要用于简化对象属性的访问和操作。这个库提供了许多实用方法,帮助开发者更方便地处理Java Bean对象。在"commons-beanutils-1.9.2-src"这个压缩包中,我们得到...

    commons-beanutil-1.7.0-src

    8. **拷贝属性**:`copyProperties()`方法允许将一个JavaBean的属性值复制到另一个JavaBean,这对于对象的克隆和属性值的传递非常有用。 9. **模块化设计**:Apache Commons BeanUtils是Apache Commons项目的一部分...

Global site tag (gtag.js) - Google Analytics