淺析Java中Apache BeanUtils和Spring BeanUtils的用法
# 前言
在我們實(shí)際項(xiàng)目開發(fā)過程中,我們經(jīng)常需要將不同的兩個(gè)對(duì)象實(shí)例進(jìn)行屬性復(fù)制,從而基于源對(duì)象的屬性信息進(jìn)行后續(xù)操作,而不改變?cè)磳?duì)象的屬性信息,比如DTO數(shù)據(jù)傳輸對(duì)象和數(shù)據(jù)對(duì)象DO,我們需要將DO對(duì)象進(jìn)行屬性復(fù)制到DTO,但是對(duì)象格式又不一樣,所以我們需要編寫映射代碼將對(duì)象中的屬性值從一種類型轉(zhuǎn)換成另一種類型。
# 對(duì)象拷貝
在具體介紹兩種 BeanUtils 之前,先來(lái)補(bǔ)充一些基礎(chǔ)知識(shí)。它們兩種工具本質(zhì)上就是對(duì)象拷貝工具,而對(duì)象拷貝又分為深拷貝和淺拷貝,下面進(jìn)行詳細(xì)解釋。
# 什么是淺拷貝和深拷貝
在Java中,除了 基本數(shù)據(jù)類型之外,還存在 類的實(shí)例對(duì)象這個(gè)引用數(shù)據(jù)類型,而一般使用 “=”號(hào)做賦值操作的時(shí)候,對(duì)于基本數(shù)據(jù)類型,實(shí)際上是拷貝的它的值,但是對(duì)于對(duì)象而言,其實(shí)賦值的只是這個(gè)對(duì)象的引用,將原對(duì)象的引用傳遞過去,他們實(shí)際還是指向的同一個(gè)對(duì)象。
而淺拷貝和深拷貝就是在這個(gè)基礎(chǔ)上做的區(qū)分,如果在拷貝這個(gè)對(duì)象的時(shí)候,只對(duì)基本數(shù)據(jù)類型進(jìn)行了拷貝,而對(duì)引用數(shù)據(jù)類型只是進(jìn)行引用的傳遞,而沒有真實(shí)的創(chuàng)建一個(gè)新的對(duì)象,則認(rèn)為是淺拷貝。反之,在對(duì)引用數(shù)據(jù)類型進(jìn)行拷貝的時(shí)候,創(chuàng)建了一個(gè)新的對(duì)象,并且復(fù)制其內(nèi)的成員變量,則認(rèn)為是深拷貝。
簡(jiǎn)單來(lái)說:
淺拷貝:對(duì)基本數(shù)據(jù)類型進(jìn)行值傳遞,對(duì)引用數(shù)據(jù)類型進(jìn)行引用傳遞般的拷貝,此為淺拷貝
深拷貝:對(duì)基本數(shù)據(jù)類型進(jìn)行值傳遞,對(duì)引用數(shù)據(jù)類型,創(chuàng)建一個(gè)新的對(duì)象,并復(fù)制其內(nèi)容,此為深拷貝。
# BeanUtils
前面簡(jiǎn)單講了一下對(duì)象拷貝的一些知識(shí),下面就來(lái)具體看下兩種 BeanUtils 工具
# Apache 的 BeanUtils
首先來(lái)看一個(gè)非常簡(jiǎn)單的BeanUtils的例子
publicclass PersonSource { private Integer id; private String username; private String password; private Integer age; // getters/setters omiited } publicclass PersonDest { private Integer id; private String username; private Integer age; // getters/setters omiited } publicclass TestApacheBeanUtils { public static void main(String[] args) throws InvocationTargetException, IllegalAccessException { //下面只是用于單獨(dú)測(cè)試 PersonSource personSource = new PersonSource(1, "pjmike", "12345", 21); PersonDest personDest = new PersonDest(); BeanUtils.copyProperties(personDest,personSource); System.out.println("persondest: "+personDest); } } persondest: PersonDest{id=1, username='pjmike', age=21}
從上面的例子可以看出,對(duì)象拷貝非常簡(jiǎn)單,BeanUtils最常用的方法就是:
//將源對(duì)象中的值拷貝到目標(biāo)對(duì)象//將源對(duì)象中的值拷貝到目標(biāo)對(duì)象 public static void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException { BeanUtilsBean.getInstance().copyProperties(dest, orig); }
但是由于 Apache下的BeanUtils對(duì)象拷貝性能太差,不建議使用,而且在阿里巴巴Java開發(fā)規(guī)約插件上也明確指出:
Ali-Check | 避免用Apache Beanutils進(jìn)行屬性的copy。
commons-beantutils 對(duì)于對(duì)象拷貝加了很多的檢驗(yàn),包括類型的轉(zhuǎn)換,甚至還會(huì)檢驗(yàn)對(duì)象所屬的類的可訪問性,可謂相當(dāng)復(fù)雜,這也造就了它的差勁的性能,具體實(shí)現(xiàn)代碼如下:
public void copyProperties(final Object dest, final Object orig) throws IllegalAccessException, InvocationTargetException { // Validate existence of the specified beans if (dest == null) { thrownew IllegalArgumentException ("No destination bean specified"); } if (orig == null) { thrownew IllegalArgumentException("No origin bean specified"); } if (log.isDebugEnabled()) { log.debug("BeanUtils.copyProperties(" + dest + ", " + orig + ")"); } // Copy the properties, converting as necessary if (orig instanceof DynaBean) { final DynaProperty[] origDescriptors = ((DynaBean) orig).getDynaClass().getDynaProperties(); for (DynaProperty origDescriptor : origDescriptors) { final String name = origDescriptor.getName(); // Need to check isReadable() for WrapDynaBean // (see Jira issue# BEANUTILS-61) if (getPropertyUtils().isReadable(orig, name) && getPropertyUtils().isWriteable(dest, name)) { final Object value = ((DynaBean) orig).get(name); copyProperty(dest, name, value); } } } elseif (orig instanceof Map) { @SuppressWarnings("unchecked") final // Map properties are always of type <String, Object> Map<String, Object> propMap = (Map<String, Object>) orig; for (final Map.Entry<String, Object> entry : propMap.entrySet()) { final String name = entry.getKey(); if (getPropertyUtils().isWriteable(dest, name)) { copyProperty(dest, name, entry.getValue()); } } } else/* if (orig is a standard JavaBean) */ { final PropertyDescriptor[] origDescriptors = getPropertyUtils().getPropertyDescriptors(orig); for (PropertyDescriptor origDescriptor : origDescriptors) { final String name = origDescriptor.getName(); if ("class".equals(name)) { continue; // No point in trying to set an object's class } if (getPropertyUtils().isReadable(orig, name) && getPropertyUtils().isWriteable(dest, name)) { try { final Object value = getPropertyUtils().getSimpleProperty(orig, name); copyProperty(dest, name, value); } catch (final NoSuchMethodException e) { // Should not happen } } } } }
# Spring 的 BeanUtils
使用spring的BeanUtils進(jìn)行對(duì)象拷貝:
publicclass TestSpringBeanUtils { public static void main(String[] args) throws InvocationTargetException, IllegalAccessException { //下面只是用于單獨(dú)測(cè)試 PersonSource personSource = new PersonSource(1, "pjmike", "12345", 21); PersonDest personDest = new PersonDest(); BeanUtils.copyProperties(personSource,personDest); System.out.println("persondest: "+personDest); } }
Spring下的BeanUtils也是使用 copyProperties方法進(jìn)行拷貝,只不過它的實(shí)現(xiàn)方式非常簡(jiǎn)單,就是對(duì)兩個(gè)對(duì)象中相同名字的屬性進(jìn)行簡(jiǎn)單的get/set,僅檢查屬性的可訪問性。具體實(shí)現(xiàn)如下:
private static void copyProperties(Object source, Object target, @Nullable Class<?> editable, @Nullable String... ignoreProperties) throws BeansException { Assert.notNull(source, "Source must not be null"); Assert.notNull(target, "Target must not be null"); Class<?> actualEditable = target.getClass(); if (editable != null) { if (!editable.isInstance(target)) { throw new IllegalArgumentException("Target class [" + target.getClass().getName() + "] not assignable to Editable class [" + editable.getName() + "]"); } actualEditable = editable; } PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable); List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null); for (PropertyDescriptor targetPd : targetPds) { Method writeMethod = targetPd.getWriteMethod(); if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) { PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName()); if (sourcePd != null) { Method readMethod = sourcePd.getReadMethod(); if (readMethod != null && ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) { try { if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) { readMethod.setAccessible(true); } Object value = readMethod.invoke(source); 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); } } } } } }
可以看到,成員變量賦值是基于目標(biāo)對(duì)象的成員列表,并且會(huì)跳過ignore的以及在源對(duì)象中不存在,所以這個(gè)方法是安全的,不會(huì)因?yàn)閮蓚€(gè)對(duì)象之間的結(jié)構(gòu)差異導(dǎo)致錯(cuò)誤,但是必須保證同名的兩個(gè)成員變量類型相同
# 小結(jié)
以上簡(jiǎn)要的分析兩種BeanUtils,因?yàn)锳pache下的BeanUtils性能較差,不建議使用,可以使用 Spring的BeanUtils ,或者使用其他拷貝框架,比如:Dozer、ModelMapper等等
到此這篇關(guān)于淺析Java中Apache BeanUtils和Spring BeanUtils的用法的文章就介紹到這了,更多相關(guān)Apache BeanUtils和Spring BeanUtils內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot+feign+Hystrix整合(親測(cè)有效)
本文主要介紹了springboot+feign+Hystrix整合,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-11-11JVM調(diào)優(yōu)參數(shù)的設(shè)置
Java虛擬機(jī)的調(diào)優(yōu)是一個(gè)復(fù)雜而關(guān)鍵的任務(wù),可以通過多種參數(shù)來(lái)實(shí)現(xiàn),本文就來(lái)介紹一下JVM調(diào)優(yōu)參數(shù)的設(shè)置,具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03SpringBoot+MinIO實(shí)現(xiàn)對(duì)象存儲(chǔ)的示例詳解
MinIO?是一個(gè)基于Apache?License?v2.0開源協(xié)議的對(duì)象存儲(chǔ)服務(wù),它是一個(gè)非常輕量的服務(wù),可以很簡(jiǎn)單的和其他應(yīng)用的結(jié)合,所以下面我們就來(lái)看看SpringBoot如何整合MinIO實(shí)現(xiàn)對(duì)象存儲(chǔ)吧2023-10-10三步輕松實(shí)現(xiàn)Java的SM2前端加密后端解密
SM2算法和RSA算法都是公鑰密碼算法,SM2算法是一種更先進(jìn)安全的算法,在我們國(guó)家商用密碼體系中被用來(lái)替換RSA算法,這篇文章主要給大家介紹了關(guān)于如何通過三步輕松實(shí)現(xiàn)Java的SM2前端加密后端解密的相關(guān)資料,需要的朋友可以參考下2024-01-01java集合類arraylist循環(huán)中刪除特定元素的方法
下面小編就為大家?guī)?lái)一篇Java集合類ArrayList循環(huán)中刪除特定元素的方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧2016-11-11