Spring的BeanUtils有坑?可能是你用錯了!
之前看到了一篇文章《用Spring的BeanUtils前,建議你先了解這幾個坑》,貌似最近還有很多公眾號在發這個,今天結合實際操作來説説我的觀點.
在這篇文章裏面,作者最後得到了這幾個結論:
-
- Spring得BeanUtils得CopyProperties方法需要對應得屬性有getter和setter方法;
-
- 如果存在屬性完全相同得內部類,但是不是同一個內部類,即分別屬於各自得內部類,則Spring會認為屬性不同,不會Copy;
-
- 泛型只在編譯期起作用,不能依靠泛型來做運行期得限制;
-
- 最後,Spring和Apache得copy屬性得方法源和目的參數得位置正好相反,所以導包和調用得時候需要注意以下。
在這裏,我們今天重點説的是第二點,第一點是因為用反射拿到set和get方法再去拿屬性值和設置屬性值的,不懂反射的人可以自行百度下。第三和第四點很簡單了應該是不需要解釋的。
驗證
首先,我把我自己的測試代碼貼出來:
@Data
public class TestEntity{
private Integer age;
private String name;
private Inner inner;
@Data
public static class Inner{
private Integer a;
public Inner(Integer a){
this.a = a;
}
}
}
複製代碼
@Data
public class TestVO{
private Integer age;
private String name;
private Inner inner;
@Data
public static class Inner{
private Integer a;
public Inner(Integer a){
this.a = a;
}
}
}
複製代碼
public class Main{
public static void main(String args[]){
TestEntity entity = new TestEntity();
entity.setAge(1);
entity.setName("hehe");
entity.setInner(new TestEntity.Inner(1));
TestVO vo = new TestVO();
BeanUtils.copyProperties(entity,vo);
System.out.println(vo.toString());
}
}
複製代碼
以上就是我得三個類,是不是超級簡單,比如工作中將entity轉vo,就有這種使用場景,運行main方法,測試結果如下
果然,和那個作者得出的第二點的結論是一樣的,b對象裏面的inner是null!
但是這個是為什麼呢?這個是BUG嗎?這個也是我今天要説的重點。
我們知道,java給我們提供了內部類這樣的東東,但是java的內部類,它其實只是java的一個語法糖而已(不知道什麼是語法糖的請自行百度),那麼我們定義得兩個JAVA類裏面的Inner的真面目到底是怎樣的呢?
來到編譯後的.class文件目錄下,我們可以看到編譯後得.class文件:
哈哈,不知道讀者到這裏是不是能明白些什麼了呢?為什麼經過BeanUtils.CopyProperties(entity,vo)之後,vo裏面的inner還是null,因為TestEntity.java和TestVO.java裏面的Inner在編譯之後的class名字都不一樣(代表加載到虛擬機之後的地址不同),怎麼可能拷貝成功呢?
那麼問題來了哈,我們怎樣用才能讓其拷貝成功呢?我稍微修改了下我的代碼如下:
@Data
public class TestVO{
private Integer age;
private String name;
private TestEntity.Inner inner;
}
複製代碼
僅僅是把Inner變為了TestEntity.Inner,刪掉了沒引用得內部類Inner,Main.java不變,然後運行結果如下:
可以看到,b對象裏面的inner被成功拷貝過來。
此時編譯後得class文件也由5個變為了4個
總結
所以説,其實人家Spring的BeanUtils.CopyProperties並沒有這種坑,坑的只是我們自己沒有掌握本質導致自己坑了自己而已。相信能看這種文章的小夥伴都是想搞懂spring或者是想提高自己開發能力的,我們在看一件事的時候,要看清楚它的本質是什麼,最少工作原理是要搞明白的,尤其是我們開發用到的東西,否則怎麼去面對一個很複雜的軟件呢?