浅析Spring浅拷贝 BeanUtils.copyProperties方法

Spring的BeanUtils.copyProperties的坑 如果需要将源对象中的属性拷贝到目标对象中,包括目标对象中继承自父类的属性,可以使用其他的 Java 对象映射工具,比如 Hutool的 BeanUtil、Apache Commons BeanUtils 和 Dozer 等。 阅读详情

最近在接口重构,因此涉及解耦,但是目前业务对象字段大多相同,因此为了节省时间,考虑使用BeanUtils.copyProperties减轻工作量,因此了解了下此方法。
大多数文章讲的是BeanUtils.copyProperties方法会拷贝相同字段属性和类型的数据,但此情况是在用户遵循Java Bean规范的前提下,但是如果用户不遵循Java Bean命名规范,或者故意利用BeanUtils.copyProperties的原理,就可以达到不一样的映射关系。
直接看源码BeanUtils.copyProperties(Object source, Object target)方法

	public static void copyProperties(Object source, Object target) throws BeansException {
			//第一个参数为源数据,第二个参数为拷贝后的数据
		copyProperties(source, target, null, (String[]) null);
	}

继续看下去

	private static void copyProperties(Object source, Object target, @Nullable Class<?> editable,
			@Nullable String... ignoreProperties) throws BeansException {
		//source和target必须自己生成
		Assert.notNull(source, "Source must not be null");
		Assert.notNull(target, "Target must not be null");
		
		Class<?> actualEditable = target.getClass();
		//如果指定了editable,则会判断target是否为editable的子类,如果不是则抛出异常。如果是,则按照父类来拷贝字段。
		if (editable != null) {
			if (!editable.isInstance(target)) {
				throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
						"] not assignable to Editable class [" + editable.getName() + "]");
			}
			actualEditable = editable;
		}
		//获取Class的get、set方法
		PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
		List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
		//下面的方法其实就是获取readMethod和writeMethod,即getXX方法和setXX方法,先getXX值,再setXX。
		for (PropertyDescriptor targetPd : targetPds) {
			Method writeMethod = targetPd.getWriteMethod();
			//判断该属性是否在不被copy的属性集合中
			if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
				PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
				if (sourcePd != null) {
					Method readMethod = sourcePd.getReadMethod();
					//判断目标对象的set方法所需的参数类型和源对象get方法的返回类型是否一致
					if (readMethod != null &&
							ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
						try {
						   //如果get方法不是public,则取消访问检查,允许调用方法。
							if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
								readMethod.setAccessible(true);
							}
							Object value = readMethod.invoke(source);
							//如果set方法不是public,则取消访问检查,允许调用方法。
							if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
								writeMethod.setAccessible(true);
							}
							writeMethod.invoke(target, value);
						}
						catch (Throwable ex) {
							throw new FatalBeanException(
									"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
						}
					}
				}
			}
		}
	}

此处简单讲解下PropertyDescriptor ,其实就是读取一个类的setXX方法和getXX方法,具体可以看这篇博客 。因此实际上BeanUtils的方法是根据比较源Class和目标Class的get和set方法。因此以下两个类也是能完成拷贝的。

public class BeanOne {
    private String test;

    public String getTest() {
        return test;
    }

    public void setTest(String test) {
        this.test = test;
    }
}

public class BeanTwo {
    private String test2;

    public String getTest() {
        return test2;
    }

    public void setTest(String test2) {
        this.test2 = test2;
    }
}

此时可以将test拷贝到test2,也可以将test2拷贝到test,但是这其实违反了Java Bean的规范的。
我们再看下Spring是如何获取PropertyDescriptor的,实际上是通过PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable)获取的。
继续看源码:

	public static PropertyDescriptor[] getPropertyDescriptors(Class<?> clazz) throws BeansException {
		CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz);
		return cr.getPropertyDescriptors();
	}

继续看下去

//简单来说就是该类会缓存一份,如果没有就构造。
static CachedIntrospectionResults forClass(Class<?> beanClass) throws BeansException {
		CachedIntrospectionResults results = strongClassCache.get(beanClass);
		if (results != null) {
			return results;
		}
		results = softClassCache.get(beanClass);
		if (results != null) {
			return results;
		}
		//关键是这里,results的构造。
		results = new CachedIntrospectionResults(beanClass);
		ConcurrentMap<Class<?>, CachedIntrospectionResults> classCacheToUse;

		if (ClassUtils.isCacheSafe(beanClass, CachedIntrospectionResults.class.getClassLoader()) ||
				isClassLoaderAccepted(beanClass.getClassLoader())) {
			classCacheToUse = strongClassCache;
		}
		else {
			if (logger.isDebugEnabled()) {
				logger.debug("Not strongly caching class [" + beanClass.getName() + "] because it is not cache-safe");
			}
			classCacheToUse = softClassCache;
		}

		CachedIntrospectionResults existing = classCacheToUse.putIfAbsent(beanClass, results);
		return (existing != null ? existing : results);
	}

继续看

在这里插入代码片
	private CachedIntrospectionResults(Class<?> beanClass) throws BeansException {
		try {
			if (logger.isTraceEnabled()) {
				logger.trace("Getting BeanInfo for class [" + beanClass.getName() + "]");
			}
			//beanInfo对象即为BeanInfo,JavaBean内省,即上述引用博客中提到的,具体可以看上面博客。
			//
			this.beanInfo = getBeanInfo(beanClass);

			if (logger.isTraceEnabled()) {
				logger.trace("Caching PropertyDescriptors for class [" + beanClass.getName() + "]");
			}
			this.propertyDescriptorCache = new LinkedHashMap<>();

			// This call is slow so we do it once.
			//其实就是通过beanInfo.getPropertyDescriptors方法获取
			PropertyDescriptor[] pds = this.beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor pd : pds) {
				if (Class.class == beanClass &&
						("classLoader".equals(pd.getName()) ||  "protectionDomain".equals(pd.getName()))) {
					// Ignore Class.getClassLoader() and getProtectionDomain() methods - nobody needs to bind to those
					continue;
				}
				if (logger.isTraceEnabled()) {
					logger.trace("Found bean property '" + pd.getName() + "'" +
							(pd.getPropertyType() != null ? " of type [" + pd.getPropertyType().getName() + "]" : "") +
							(pd.getPropertyEditorClass() != null ?
									"; editor [" + pd.getPropertyEditorClass().getName() + "]" : ""));
				}
				pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
				this.propertyDescriptorCache.put(pd.getName(), pd);
			}

			// Explicitly check implemented interfaces for setter/getter methods as well,
			// in particular for Java 8 default methods...
			Class<?> currClass = beanClass;
			while (currClass != null && currClass != Object.class) {
				introspectInterfaces(beanClass, currClass);
				currClass = currClass.getSuperclass();
			}

			this.typeDescriptorCache = new ConcurrentReferenceHashMap<>();
		}
		catch (IntrospectionException ex) {
			throw new FatalBeanException("Failed to obtain BeanInfo for class [" + beanClass.getName() + "]", ex);
		}
	}

接下来我们看beanInfo怎么获得,因此BeanInfo是接口,需要有具体实现。继续看 this.beanInfo = getBeanInfo(beanClass);

	private static BeanInfo getBeanInfo(Class<?> beanClass) throws IntrospectionException {
		for (BeanInfoFactory beanInfoFactory : beanInfoFactories) {
		 	//即遍历beanInfoFactories,如果有对应的能够加载该类的,则返回BeanInfo。
			BeanInfo beanInfo = beanInfoFactory.getBeanInfo(beanClass);
			if (beanInfo != null) {
				return beanInfo;
			}
		}
		return (shouldIntrospectorIgnoreBeaninfoClasses ?
				Introspector.getBeanInfo(beanClass, Introspector.IGNORE_ALL_BEANINFO) :
				Introspector.getBeanInfo(beanClass));
	}

再来看beanInfoFactory

	//即读取spring.factories的BeanInfoFactory的实现类
	private static List<BeanInfoFactory> beanInfoFactories = SpringFactoriesLoader.loadFactories(
			BeanInfoFactory.class, CachedIntrospectionResults.class.getClassLoader());

点击看spring-beans的jar包下的META-INF中spring.factories可知,即获取到的是ExtendedBeanInfoFactory

org.springframework.beans.BeanInfoFactory=org.springframework.beans.ExtendedBeanInfoFactory

观看上述对应的源码

	//即实际上还是调用JAVA BEAN 内省工具Introspector,相关描述在上述引用的博客中有提到。
	public BeanInfo getBeanInfo(Class<?> beanClass) throws IntrospectionException {
		return (supports(beanClass) ? new ExtendedBeanInfo(Introspector.getBeanInfo(beanClass)) : null);
	}

总结: BeanUtils.copyProperties是浅拷贝的一种,通过调用set和get方法实现拷贝,他们的拷贝并不是根据字段名和字段类型来判断的,而是根据set、get名和属性判断。

【Java】Spring中BeanUtils.copyProperties的坑及解决 如果需要将源对象中的属性拷贝到目标对象中,包括目标对象中继承自父类的属性,可以使用其他的 Java 对象映射工具,比如 Hutool的 BeanUtil、Apache Commons BeanUtils 和 Dozer 等。方法是基于 Java 反射实现的,它可以将源对象中的属性拷贝到目标对象中,并支持类型转换和自定义转换器。方法是基于 Java 反射实现的,它只能访问源对象中的属性,无法访问目标对象中继承自父类的属性。方法进行属性拷贝时,只会拷贝源对象中定义的属性,而不会拷贝目标对象中继承自父类的属性。 阅读详情

相关推荐

Spring BeanUtils.copyProperties实现机制

Spring框架中的方法提供了一种在两个 Java 对象之间复制属性的便捷方式。与 Apache Commons BeanUtils 类似,它也是基于反射来实现的。

Enzo的探索之路 1547

BeanUtils.copyProperties() 详解

BeanUtils.copyProperties会进行类型转换; BeanUtils.copyProperties方法简单来说就是将两个字段相同的对象进行属性值的复制。 这两个类在不同的包下面,而这两个类的copyProperties()方法里面传递的参数赋值是相反的。 一、 org.springframework.beans.BeanUtils a拷贝到b a,b为对象 BeanUtils.copyProperties(a, b); 二、org.apache.commons.beanutils.Bean

wjw_de_java的博客 7万+

【安装文档】TRex流量分析仪保姆级安装指南--基于VMware虚拟机(ubantu18.04@Intel 82545EM

如果之前添加了网卡,这里就可以看见有3个网卡(没添加的现在再加也行),* Active *为Linux内核正在使用的网卡,另外两张是未启用的网卡(注:未启用的网卡在终端中使用ifconfig命令是查不到的,需要先使用ip addr找出网卡,再激活,此时网卡会变为* Active *状态。

lingshengxiyou的博客 2130

BeanUtils.copyProperties的用法(超详细,建议收藏)

用法 org.springframework.beans.BeanUtils,它提供了对java反射和自省API的包装。它里面还有很多工具类,这里我们介绍一下copyProperties。 我们如果有两个具有很多相同属性的JavaBean对象a和b,想把a中的属性赋值到b,比如接口中将接收到的前端请求参数封装为reqVo,dan @Data public class User { private String id; private String name; private Stri

wwwwwww31311的博客 6万+

HWDB1.1数据集处理全攻略:从gnt文件到PNG图片的完整转换教程

本文提供了HWDB1.1数据集处理的完整教程,详细解析了gnt二进制文件格式,并给出了从文件解析、标签映射到批量转换为PNG图片的Python脚本。教程涵盖了环境准备、跨平台解压方案,以及为PyTorch等深度学习框架准备数据的最佳实践,是进行手写汉字识别研究不可或缺的实用指南。

2w3e4r5t6y的博客 627

保姆级教程:用Ubuntu 22.04搭建RustDesk中继服务器(含Cloudflare避坑指南)

本教程详细指导如何在Ubuntu 22.04上搭建RustDesk中继服务器,解决跨国远程协作的延迟问题。涵盖环境配置、Rust编译优化、服务部署及Cloudflare代理设置等关键步骤,帮助用户实现高性能的私有中继服务。

weixin_29002209的博客 526

copyproperties用法_浅析Java中Apache BeanUtils和Spring BeanUtils的用法_java

这篇文章主要介绍了Java中Apache BeanUtils和Spring BeanUtils的用法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下# 前言在我们实际项目开发过程中,我们经常需要将不同的两个对象实例进行属性复制,从而基于源对象的属性信息进行后续操作,而不改变源对象的属性信息,比如DTO数据传输对象和数据对象DO,我们需要将DO对...

weixin_39764212的博客 230

Python 中 dpkt 库的详细使用指南(强大的 Python 数据包解析库)

dpkt 是一个功能强大的 Python 网络数据包解析库,支持解析和构造以太网、IP、TCP/UDP 等多种协议的数据包。其核心功能包括解析各层协议头部、读写 pcap 文件、构造自定义数据包及分析 HTTP 流量等。dpkt 提供简单易用的 API,适用于网络分析、安全研究和流量监控场景。优化技巧包括数据包过滤、批量处理和选择性解析以提高性能,常见问题如数据不完整或协议识别错误可通过异常处理和字段验证解决。典型应用场景涵盖协议分析、网络调试和自定义数据包生成。

数据知道的博客 1万+

BeanUtils.copyProperties

简介 这里说的是spring的BeanUtils.copyProperties BeanUtils 提供对 Java 反射和自省 API 的包装,其主要目的是利用反射机制对 JavaBean 的属性进行处理 public static void copyProperties(Object source, Object target) throws BeansException { copyProperties(source, target, (Class)null, (String[])null);

qq_40813329的博客 1万+

基于STM32的嵌入式平台实现EtherCAT主站

本文将介绍如何在基于STM32的嵌入式平台上构建EtherCAT主站,并提供相应的源代码。使用SOEM库提供的API函数,我们可以实现与EtherCAT从站设备的实时通信和数据交换。在STM32的代码中,使用SOEM库提供的API函数初始化EtherCAT主站。在SOEM库中,我们可以找到EtherCAT主站的实现代码和相关的API函数。使用SOEM库提供的API函数实现EtherCAT主站的功能,包括发送和接收EtherCAT数据帧、处理从站设备的状态等。确保连接正常并且设备能够相互通信。

CodeSageX的博客 2832

EtherCAT用TwinCAT 3跑DC报0x2C或者0x1A错误码

EtherCAT用TwinCAT 3跑DC报0x2C或者0x1A DC报错 ///////////////////////////////////////////////////////////////////////////////////////// /** \brief Checks communication and synchronisation variables and update AL status / AL status code if an error has occurr

PI_sunyang的博客 5304

Java BeanUtils.copyProperties() 详解及应用

方法用于将一个 Java 对象的属性复制到另一个对象。它在处理数据传输对象(DTO)、视图对象(VO)和实体对象(Entity)之间的数据传递时非常有用。source:源对象,属性值从这个对象中提取。target:目标对象,将属性值复制到这个对象中。是一个强大的工具,可以简化对象属性的拷贝操作。它在 Java 开发中扮演着重要的角色,尤其是在对象转换、数据封装和自动化测试等场景中。虽然它提供了很大的便利,但在性能要求较高的情况下,可能需要评估其他解决方案。

m0_73467713的博客 1万+

RNA-seq分析:Step4(比对)

预处理之后的fastq文件,可以使用HISAT2软件将reads比对至参考基因组上。HISAT2软件将reads比对至参考基因组上的步骤如下:1. 对参考基因组进行建索引。对参考基因组进行预处理,生成索引文件,包括基因组序列的FM索引和SA索引。2. 将原始的reads进行预处理。包括去除接头序列、去除低质量的碱基,以及进行过滤,得到质量较高的、可靠的reads。3. 利用建立的FM索引和SA索引,将处理后的reads与参考基因组进行比对。首先,通过FM索引,快速找到与reads匹配的可能位置;

沉香best的博客 2599

Spring学习笔记(一)【BeanUtils.copyProperties方法】

简单地说,拷贝就是将一个类中的属性拷贝到另一个中,对于BeanUtils.copyProperties来说,你必须保证属性名和类型是相同的,因为它是根据get和set方法来赋值的1.1、浅拷贝浅拷贝对于基本数据类型就是直接进行值传递,在内存的另一个空间内存放,修改这个值不会影响到拷贝源的值浅拷贝对于引用数据类型就是进行的是地址传递,并没有对该对象重新开辟一个内存空间进行存放,所以对于引用数据类型的浅拷贝就相当于两个引用指向了同一个内存地址。

Vinjcent 1万+

Python全栈入门到实战【JavaScript篇 15】async/await与Fetch API,打通前后端数据通道

Fetch API是浏览器内置的现代HTTP请求接口,用于向服务器发送请求并获取响应。它是(XHR)的替代品。学习Fetch的目标非常明确——用JS从后端API获取数据,并渲染到前端页面。这标志着前后端数据通道的打通,是实现全栈开发的核心技能。try {// 等待Promiseconst parallel = await Promise.all([ // 并行等待]);

那些看似不起波澜的日复一日,会在某天让你看到坚持的意义 1978

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

BeanUtils.copyProperties基本用法与小坑

(1)实际应用 BeanUtils.copyProperties(赋值目标对象,模板源对象); 我们都知道当有两个对象AB,属性名称一样的情况下,如果需要把A的全部属性给B,传统写法是: B.setXX(A.getXX()); 如果只有几个特别的参数需要赋值过去这样写没问题,如果AB有100个参数需要粘过去,那么代码就太冗余太长了,BeanUtils.copyProperties就是为了解决这个问题的,现在假设有AB两个对象: static class DemoA { private String

whiteBearClimb的博客 1万+

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

BeanUtils工具类中的copyProperties方法使用

文章目录1、两个包下的BeanUtils.copyProperties对比2、BeanUtils.copyProperties的深浅拷贝问题2.1、浅拷贝和深拷贝2.2、BeanUtils.copyProperties深浅拷贝问题 1、两个包下的BeanUtils.copyProperties对比 BeanUtils是开发中常用到的工具类,而获取这一工具类主要是通过导入org.springframework.beans.BeanUtils或者org.apache.commons.beanutils.Bean

qgnczmnmn的博客 9万+

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Beanutils.copyProperties( )使用遇到的问题

Beanutils.copyProperties( )使用遇到的问题 BeanUtils.copyProperties VS PropertyUtils.copyProperties 两者最大的区别是: BeanUtils.copyProperties会进行类型转换,而PropertyUtils.copyProperties不会。 既然进行了类型转换,那BeanUtils.copyProperties的速度比不上PropertyUtils.copyProperties。 因此,PropertyUtils

wangjianweiaaa的博客 4212

JAVA基础:Spring的BeanUtils.copyProperties的使用(深拷贝,浅拷贝)

看了下发现是 dest,src ,于是果断使用,结果发现参数没了。这就是浅拷贝,无忌用的life引用的翠山的life对象。BeanUtils.copyProperties的使用(深拷贝,浅拷贝) - 小知的文章 - 知乎。因为他们2个的src和dest是正好相反的,要特别留意。来源:BeanUtils.copyProperties的使用(深拷贝,浅拷贝)虽然有子对象,但是子对象并不怎么改动,那么用BeanUtils也是没问题的。如果都是单一的属性,那么不涉及到深拷贝的问题,适合用BeanUtils。....

qq_36320053的博客 9244

BeanUtils.copyProperties的11个坑

前言 大家好,我是田螺。我们日常开发中,经常涉及到DO、DTO、VO对象属性拷贝赋值,很容易想到org.springframework.beans.BeanUtils的copyProperties 。它会自动通过反射机制获取源对象和目标对象的属性,并将对应的属性值进行复制。可以减少手动编写属性复制代码的工作量,提高代码的可读性和维护性。但是你知道嘛?使用BeanUtils的copyProperti...

weixin_44742132的博客 749

BeanUtils.copyProperties方法详解

方法是 Apache Commons BeanUtils 库提供的一个实用方法,用于将一个 Java 对象的属性值复制到另一个 Java 对象中。它的作用是简化对象之间属性复制的过程,避免手动编写大量的赋值代码。该方法会将源对象的属性值复制到目标对象中,属性名相同的属性会被复制。下面是一个示例,演示了如何使用。的属性值复制到了目标对象。

是木子啦~ 3173

Beanutils.copyproperties 拷贝类 BeanUtil.convertListToList 拷贝集合

package com.ebj.wwk.core.toolbox.utils; import com.alibaba.fastjson.JSON; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import lombok

qq_43035664的博客 9991

别再乱用了,BeanUtils.copyProperties 使用中的这些坑你一定要知道

我们日常开发中,经常涉及到DO、DTO、VO对象属性拷贝赋值,很容易想到的。它会自动通过反射机制获取源对象和目标对象的属性,并将对应的属性值进行复制。可以减少手动编写属性复制代码的工作量,提高代码的可读性和维护性。但是你知道嘛?使用BeanUtils的。

qq_43842093的博客 5313

JAVA字段复制工具 BeanUtils 的 copyProperties

常用工具-字段拷贝赋值 – 用于相同字段值的考培拷贝(不同字段解决方案请往下看) BeanUtils的copyProperties()方法有四种重载方式 1.copyProperties(Object source, Object target) 2.copyProperties(Object source, Object target, Class<?> editable) 3.copyProperties(Object source, Object target, String… igno

ywh22122的博客 3355
上一篇: 浅析java String类型
下一篇: Optional的应用
立志成为架构师
博客等级 码龄8年 25粉丝 62原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值