日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

spring BeanUtils.copyProperties浅拷贝之特殊的Boolean

發布時間:2024/4/11 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 spring BeanUtils.copyProperties浅拷贝之特殊的Boolean 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

背景

在開發過程中, 在數據庫查詢和接口調用過程中,為了隔離,我們往往會將查詢出來的對象(包括數據庫返回和接口返回)和對外提供的實體對象隔離開來。此時就需要把一個對象的屬性拷貝到目標對象中
通常有2種做法:
1、一個一個set b.setField(a.getField());
2、使用拷貝的工具類,比方說 BeanUtils.copyProperties (因為他們的類結構和屬性字段大多是類似的)

對于字段比較多的場景,使用BeanUtils明顯更加簡潔

那么問題來了,
BeanUtils對于對象中包含的對象也能夠幫我們進行拷貝嗎?
如果會進行拷貝,它幫我們做的拷貝是深拷貝還是淺拷貝?

深淺拷貝 的主要區別就是:復制的是引用還是復制的是值。

如果僅僅復制了引用,也就是說,復制之后,原來的變量和新的變量指向同一個地址,彼此之間的操作會互相影響,為 淺拷貝。
而如果是在堆中重新分配內存,擁有不同的地址,但是值是一樣的,復制后的對象與原來的對象是完全隔離,互不影響,就是深拷貝。

BeansUtils 實戰

創建兩個類 Student和Course 以及一個目標類StudentEntity(和Student類除了名字以外其余字段和結構都完全一致)

public class Student {private String name;private Integer age;/*** 是否已畢業* */private Boolean isGraduated;private Course course;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public Boolean getGraduated() {return isGraduated;}public void setGraduated(Boolean graduated) {isGraduated = graduated;}public Course getCourse() {return course;}public void setCourse(Course course) {this.course = course;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", isGraduated=" + isGraduated +", course=" + course +'}';} public class Course {private String courseName;private Integer score;public String getCourseName() {return courseName;}public void setCourseName(String courseName) {this.courseName = courseName;}public Integer getScore() {return score;}public void setScore(Integer score) {this.score = score;}@Overridepublic String toString() {return "Course{" +"courseName='" + courseName + '\'' +", score=" + score +'}';} }

測試類:

public class Test {@org.junit.Testpublic void testCopyStudent(){Student student = new Student();student.setName("chenpp");student.setAge(21);student.setGraduated(true);Course course = new Course();course.setCourseName("Chinese");course.setScore(100);student.setCourse(course);StudentEntity copyStudent = new StudentEntity();BeanUtils.copyProperties(student, copyStudent);System.out.println("拷貝后的對象" + copyStudent);copyStudent.getCourse().setCourseName("Math");System.out.println("修改拷貝后的course,原來的對象" + student);} }


從上面的代碼可以看出,spring-beans提供的BeanUtils.copyProperties對于外層的屬性無論是基本數據類型還是引用數據類型都會進行拷貝,使用的是淺拷貝

Boolean和boolean類型的拷貝

在使用過程中,發現如果Student的isGraduated字段的set方法定義成isGraduated(),那么拷貝就會失敗

public class Student {private String name;private Integer age;/*** 是否已畢業* */private Boolean isGraduated;private Course course;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public Boolean isGraduated() {return isGraduated;}public void setIsGraduated(Boolean isGraduated) {this.isGraduated = isGraduated;} }

測試結果: isGraduated字段沒有被拷貝

跟蹤下源碼看看是什么原因?

private static void copyProperties(Object source, Object target, Class<?> editable, String... ignoreProperties)throws BeansException {Class<?> actualEditable = target.getClass();//...//根據targetClass獲取其屬性描述器PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);//...for (PropertyDescriptor targetPd : targetPds) {Method writeMethod = targetPd.getWriteMethod();if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {//根據sourceClass獲取其屬性描述器(和targetClass執行的方法一樣)PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());if (sourcePd != null) {//如果源對象的readMethod為空,就無法獲取到源對象的值,也就無法通過反射進行賦值Method readMethod = sourcePd.getReadMethod();if (readMethod != null &&ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {try {//...writeMethod.invoke(target, value);}catch (Throwable ex) {}}}}}}

其源碼路徑如下
BeanUtils.getPropertyDescriptor -->
? ? ?CachedIntrospectionResults.forClass(clazz) -->
? ? ?? ? ? new CachedIntrospectionResults(beanClass) -->
? ? ?? ? ?? ? ? Introspector.getBeanInfo(Class<?> beanClass) -->
? ? ?? ? ?? ? ?? ? ? Introspector.getBeanInfo()

private BeanInfo getBeanInfo() throws IntrospectionException {// the evaluation order here is import, as we evaluate the// event sets and locate PropertyChangeListeners before we// look for properties.BeanDescriptor bd = getTargetBeanDescriptor();MethodDescriptor mds[] = getTargetMethodInfo();EventSetDescriptor esds[] = getTargetEventInfo();//關鍵方法,獲取目標對象的屬性信息 這里就是StudentEntityPropertyDescriptor pds[] = getTargetPropertyInfo();int defaultEvent = getTargetDefaultEventIndex();int defaultProperty = getTargetDefaultPropertyIndex();return new GenericBeanInfo(bd, esds, defaultEvent, pds,defaultProperty, mds, explicitBeanInfo);} private PropertyDescriptor[] getTargetPropertyInfo() {Method methodList[] = getPublicDeclaredMethods(beanClass);for (int i = 0; i < methodList.length; i++) {//...Class<?>[] argTypes = method.getParameterTypes();Class<?> resultType = method.getReturnType();int argCount = argTypes.length;//...if (argCount == 0) {if (name.startsWith(GET_PREFIX)) {// Simple getterpd = new PropertyDescriptor(this.beanClass, name.substring(3), method, null);} else if (resultType == boolean.class && name.startsWith(IS_PREFIX)) {// Boolean getterpd = new PropertyDescriptor(this.beanClass, name.substring(2), method, null);}}//...}}

從這里就可以看到,如果屬性是Boolean類型,其read方法需要以getXXX命名,而對于boolean類型,需要以isXX命名

根據class獲取對應的屬性信息之后,會緩存在propertyDescriptorCache里,debug可以看到StudentEntity的isGraduated字段對應的readMethod為空(Student的isGraduated字段對應的readMethod也為空)

總結

BeanUtils.copyProperties主要是通過反射實現的淺拷貝,如果對象都是單一的屬性或者子對象不涉及到改動,可以BeanUtils進行拷貝
在拷貝的時候注意對于Boolean類型字段,其readMethod需要以getXXX來命名

總結

以上是生活随笔為你收集整理的spring BeanUtils.copyProperties浅拷贝之特殊的Boolean的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。