Java?Bean所有拷貝方式使用方法及性能比較詳解
一、總體概述及比較
1. 手動(dòng)拷貝
方式說(shuō)明最原始也最靈活的方法,直接通過(guò)setter和getter手動(dòng)賦值。
示例代碼
TargetBean target = new TargetBean(); target.setName(source.getName()); target.setAge(source.getAge());
優(yōu)點(diǎn)
- 靈活性高,可以自定義轉(zhuǎn)換邏輯
- 不依賴第三方庫(kù)
缺點(diǎn)
- 代碼冗余,維護(hù)成本高
- 屬性多時(shí)工作量大,易出錯(cuò)
適用場(chǎng)景
- 屬性復(fù)雜、轉(zhuǎn)換邏輯多樣時(shí)
- 對(duì)性能和依賴有嚴(yán)格要求時(shí)
2. Java BeanUtils 工具類
2.1 Apache Commons BeanUtils
方式說(shuō)明使用 org.apache.commons.beanutils.BeanUtils 進(jìn)行屬性拷貝。
示例代碼
import org.apache.commons.beanutils.BeanUtils; BeanUtils.copyProperties(target, source);
優(yōu)點(diǎn)
- 簡(jiǎn)單易用
- 支持不同類型的對(duì)象拷貝
缺點(diǎn)
- 依賴反射,性能較低
- 不支持基本類型,全部按字符串處理,類型轉(zhuǎn)換有限
- 不支持嵌套對(duì)象深拷貝
適用場(chǎng)景
- 屬性簡(jiǎn)單、性能要求不高的場(chǎng)合
2.2 Spring BeanUtils
方式說(shuō)明使用 org.springframework.beans.BeanUtils 進(jìn)行屬性拷貝。
示例代碼
import org.springframework.beans.BeanUtils; BeanUtils.copyProperties(source, target);
優(yōu)點(diǎn)
- 性能比 Apache BeanUtils 略高
- 支持基本類型
- Spring 項(xiàng)目中開箱即用
缺點(diǎn)
- 依然是淺拷貝,不支持嵌套對(duì)象
- 對(duì)屬性名和類型要求嚴(yán)格
適用場(chǎng)景
- Spring 項(xiàng)目中,簡(jiǎn)單Bean轉(zhuǎn)換
3. CGLIB BeanCopier
方式說(shuō)明使用 CGLIB 的 BeanCopier,通過(guò)動(dòng)態(tài)生成字節(jié)碼實(shí)現(xiàn)高性能拷貝。
示例代碼
import net.sf.cglib.beans.BeanCopier; BeanCopier copier = BeanCopier.create(Source.class, Target.class, false); copier.copy(source, target, null);
優(yōu)點(diǎn)
- 性能高于反射方式
- 支持同名同類型屬性的快速拷貝
缺點(diǎn)
- 依賴CGLIB庫(kù)
- 不支持類型轉(zhuǎn)換和嵌套對(duì)象
- 代碼略繁瑣
適用場(chǎng)景
- 需要批量高性能拷貝的場(chǎng)景
4. MapStruct
方式說(shuō)明MapStruct 是編譯期代碼生成的Bean拷貝工具,支持復(fù)雜映射和自定義轉(zhuǎn)換。
示例代碼
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
TargetBean toTargetBean(SourceBean source);
}使用:
TargetBean target = UserMapper.INSTANCE.toTargetBean(source);
優(yōu)點(diǎn)
- 編譯期生成代碼,性能極高
- 支持復(fù)雜屬性映射、類型轉(zhuǎn)換
- 可自定義字段映射規(guī)則
缺點(diǎn)
- 需要編寫接口和注解,學(xué)習(xí)成本略高
- 需要編譯期插件支持
適用場(chǎng)景
- 復(fù)雜對(duì)象轉(zhuǎn)換、大型項(xiàng)目、性能敏感場(chǎng)合
5. Dozer
方式說(shuō)明Dozer 是一個(gè)支持深度拷貝的 Java Bean 到 Java Bean 的映射工具。
示例代碼
Mapper mapper = new DozerBeanMapper(); TargetBean target = mapper.map(source, TargetBean.class);
優(yōu)點(diǎn)
- 支持深度拷貝
- 支持復(fù)雜類型映射
- 支持XML配置映射規(guī)則
缺點(diǎn)
- 性能一般,反射實(shí)現(xiàn)
- 配置和調(diào)試較復(fù)雜
適用場(chǎng)景
- 需要深度拷貝和復(fù)雜映射的場(chǎng)景
6. ModelMapper
方式說(shuō)明ModelMapper 也是一個(gè)Java Bean映射工具,支持復(fù)雜映射和類型轉(zhuǎn)換。
示例代碼
ModelMapper modelMapper = new ModelMapper(); TargetBean target = modelMapper.map(source, TargetBean.class);
優(yōu)點(diǎn)
- 支持復(fù)雜映射和深拷貝
- 支持自定義映射規(guī)則
缺點(diǎn)
- 性能一般,反射實(shí)現(xiàn)
- 配置略復(fù)雜
適用場(chǎng)景
- 對(duì)轉(zhuǎn)換靈活性要求較高的場(chǎng)景
7. Lombok @Builder/@Data + 構(gòu)造方法
方式說(shuō)明利用 Lombok 注解簡(jiǎn)化代碼,通過(guò)構(gòu)造方法或Builder模式實(shí)現(xiàn)對(duì)象轉(zhuǎn)換。
示例代碼
TargetBean target = TargetBean.builder()
.name(source.getName())
.age(source.getAge())
.build();優(yōu)點(diǎn)
- 代碼簡(jiǎn)潔
- 可自定義轉(zhuǎn)換邏輯
缺點(diǎn)
- 需手動(dòng)指定字段
- 適合屬性較少、轉(zhuǎn)換邏輯簡(jiǎn)單時(shí)
8. JSON序列化反序列化
方式說(shuō)明通過(guò)Jackson、Gson等將對(duì)象轉(zhuǎn)為JSON再反序列化為目標(biāo)類型。
示例代碼
ObjectMapper mapper = new ObjectMapper(); TargetBean target = mapper.readValue(mapper.writeValueAsString(source), TargetBean.class);
優(yōu)點(diǎn)
- 支持深度拷貝
- 屬性名不一致時(shí)可通過(guò)注解映射
缺點(diǎn)
- 性能較低
- 依賴第三方庫(kù)
- 不適合大批量高頻率場(chǎng)景
9. 總結(jié)對(duì)比表
| 方式 | 性能 | 深拷貝 | 復(fù)雜映射 | 依賴庫(kù) | 適用場(chǎng)景 |
|---|---|---|---|---|---|
| 手動(dòng)拷貝 | 高 | 支持 | 支持 | 無(wú) | 靈活性強(qiáng)、屬性少 |
| BeanUtils | 低 | 否 | 否 | commons/spring | 簡(jiǎn)單Bean拷貝 |
| CGLIB BeanCopier | 高 | 否 | 否 | cglib | 大批量拷貝 |
| MapStruct | 極高 | 支持 | 支持 | mapstruct | 大型項(xiàng)目、復(fù)雜轉(zhuǎn)換 |
| Dozer | 一般 | 支持 | 支持 | dozer | 深拷貝、復(fù)雜映射 |
| ModelMapper | 一般 | 支持 | 支持 | modelmapper | 靈活映射 |
| Lombok Builder | 高 | 否 | 支持 | lombok | 屬性少、代碼簡(jiǎn)潔 |
| JSON序列化 | 低 | 支持 | 支持 | jackson/gson | 臨時(shí)、簡(jiǎn)單深拷貝 |
10. 選擇建議
- 簡(jiǎn)單場(chǎng)景:Spring BeanUtils、CGLIB BeanCopier
- 復(fù)雜映射:MapStruct、Dozer、ModelMapper
- 深拷貝:Dozer、ModelMapper、JSON序列化
- 性能敏感:MapStruct、CGLIB BeanCopier、手動(dòng)拷貝
- 靈活性高:手動(dòng)拷貝、Lombok Builder
二、使用詳解
1、Spring BeanUtils 詳細(xì)用法
1.1. 基本用法
import org.springframework.beans.BeanUtils; SourceBean source = new SourceBean(); TargetBean target = new TargetBean(); BeanUtils.copyProperties(source, target);
注意:屬性名和類型需完全一致,且只做淺拷貝(即對(duì)象屬性只拷貝引用)。
1.2. 拷貝部分屬性
可以指定忽略某些屬性:
BeanUtils.copyProperties(source, target, "password", "id");
1.3. 常見(jiàn)問(wèn)題
- 不支持深拷貝:嵌套對(duì)象只拷貝引用。
- 類型不兼容會(huì)報(bào)錯(cuò):如
int和String不能自動(dòng)轉(zhuǎn)換。
2、Apache Commons BeanUtils 詳細(xì)用法
2.1. 基本用法
import org.apache.commons.beanutils.BeanUtils; BeanUtils.copyProperties(target, source);
2.2. 類型轉(zhuǎn)換
BeanUtils會(huì)自動(dòng)做一些類型轉(zhuǎn)換(如 String 轉(zhuǎn) int),但不支持復(fù)雜類型。
2.3. 性能問(wèn)題
BeanUtils 采用反射,性能較低,不推薦在高頻場(chǎng)景下使用。
3、CGLIB BeanCopier 進(jìn)階用法
3.1. 基本用法
BeanCopier copier = BeanCopier.create(SourceBean.class, TargetBean.class, false); copier.copy(source, target, null);
3.2. 支持自定義轉(zhuǎn)換
BeanCopier copier = BeanCopier.create(SourceBean.class, TargetBean.class, true);
copier.copy(source, target, (value, targetType, context) -> {
// 自定義轉(zhuǎn)換邏輯
if (value instanceof String && targetType == Integer.class) {
return Integer.valueOf((String)value);
}
return value;
});3.3. 性能優(yōu)勢(shì)
BeanCopier會(huì)動(dòng)態(tài)生成字節(jié)碼,性能極高,適合大批量拷貝。
4、MapStruct 進(jìn)階用法
4.1. 基本映射
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
TargetBean toTargetBean(SourceBean source);
}4.2. 字段名不一致映射
@Mapper
public interface UserMapper {
@Mapping(source = "username", target = "name")
TargetBean toTargetBean(SourceBean source);
}4.3. 自定義類型轉(zhuǎn)換
@Mapper
public interface UserMapper {
@Mapping(source = "age", target = "ageStr")
TargetBean toTargetBean(SourceBean source);
default String intToString(int age) {
return String.valueOf(age);
}
}4.4. 集合轉(zhuǎn)換
List<TargetBean> toTargetBeanList(List<SourceBean> sourceList);
4.5. 注意事項(xiàng)
- 需要在IDE或maven插件中開啟注解處理器。
- 生成代碼后可直接調(diào)用,性能極高。
5、Dozer/ModelMapper 進(jìn)階用法
5.1. Dozer XML 配置
<mapping>
<class-a>com.example.SourceBean</class-a>
<class-b>com.example.TargetBean</class-b>
<field>
<a>username</a>
<b>name</b>
</field>
</mapping>5.2. ModelMapper 映射規(guī)則
modelMapper.typeMap(SourceBean.class, TargetBean.class)
.addMappings(mapper -> mapper.map(SourceBean::getUsername, TargetBean::setName));5.3. 支持復(fù)雜對(duì)象和嵌套屬性
適合復(fù)雜領(lǐng)域模型轉(zhuǎn)換,但性能一般。
6、JSON序列化方式進(jìn)階
6.1. Jackson
ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(source); TargetBean target = mapper.readValue(json, TargetBean.class);
6.2. Gson
Gson gson = new Gson(); String json = gson.toJson(source); TargetBean target = gson.fromJson(json, TargetBean.class);
6.3. 注意事項(xiàng)
- 適合臨時(shí)轉(zhuǎn)換或深拷貝,但性能較低。
- 可通過(guò)注解如
@JsonProperty實(shí)現(xiàn)字段名映射。
7、Lombok Builder/構(gòu)造方法進(jìn)階
7.1. Lombok簡(jiǎn)化代碼
@Getter
@Setter
@Builder
public class TargetBean {
private String name;
private int age;
}拷貝:
TargetBean target = TargetBean.builder()
.name(source.getName())
.age(source.getAge())
.build();7.2. 適合場(chǎng)景
- 屬性較少、轉(zhuǎn)換邏輯簡(jiǎn)單時(shí)
- 代碼可讀性高,維護(hù)方便
8、實(shí)戰(zhàn)建議
- 簡(jiǎn)單DTO/VO轉(zhuǎn)換:優(yōu)先用Spring BeanUtils或CGLIB BeanCopier。
- 復(fù)雜/多字段/類型轉(zhuǎn)換:優(yōu)先MapStruct。
- 深拷貝或嵌套對(duì)象:Dozer、ModelMapper或JSON序列化。
- 性能敏感批量轉(zhuǎn)換:CGLIB BeanCopier、MapStruct。
- 自定義轉(zhuǎn)換邏輯多:手動(dòng)拷貝或MapStruct自定義方法。
9、注意事項(xiàng)與最佳實(shí)踐
- 避免在高并發(fā)場(chǎng)景用反射類工具(BeanUtils、Dozer等)。
- 字段名/類型不一致要選支持自定義映射的工具(MapStruct、Dozer、ModelMapper)。
- 拷貝前后對(duì)象不可互相影響(深拷貝需求)時(shí)要選支持深拷貝的工具。
- 轉(zhuǎn)換邏輯復(fù)雜時(shí)建議寫單元測(cè)試,避免數(shù)據(jù)丟失或錯(cuò)誤。
三、常見(jiàn)問(wèn)題與解決辦法
1. 字段名不一致
問(wèn)題:源對(duì)象和目標(biāo)對(duì)象字段名不同,常規(guī)工具(如Spring BeanUtils)無(wú)法自動(dòng)拷貝。
解決方案:
- MapStruct支持@Mapping注解自定義字段映射。
- Dozer和ModelMapper可通過(guò)配置或API自定義映射。
- 手動(dòng)拷貝最靈活,直接賦值。
2. 類型不一致
問(wèn)題:如源對(duì)象字段為String,目標(biāo)對(duì)象為Integer。
解決方案:
- MapStruct可自定義轉(zhuǎn)換方法。
- CGLIB BeanCopier可通過(guò)轉(zhuǎn)換器接口自定義類型轉(zhuǎn)換。
- ModelMapper支持自定義轉(zhuǎn)換規(guī)則。
3. 嵌套對(duì)象或集合
問(wèn)題:如源對(duì)象有嵌套對(duì)象或集合,淺拷貝只拷貝引用。
解決方案:
- Dozer、ModelMapper、JSON序列化支持深拷貝。
- MapStruct支持嵌套對(duì)象和集合的轉(zhuǎn)換(可遞歸映射)。
4. 性能瓶頸
問(wèn)題:反射類工具在大數(shù)據(jù)量場(chǎng)景下性能不足。
解決方案:
- MapStruct、CGLIB BeanCopier編譯期或字節(jié)碼級(jí)別實(shí)現(xiàn),性能極高。
- 手動(dòng)拷貝性能最佳,但開發(fā)成本高。
5. Null值處理
問(wèn)題:拷貝時(shí)源對(duì)象字段為null,目標(biāo)對(duì)象是否需要覆蓋?
解決方案:
- BeanUtils默認(rèn)會(huì)拷貝null,可以通過(guò)擴(kuò)展或自定義工具類實(shí)現(xiàn)跳過(guò)null。
- MapStruct支持@Mapping(target = “xxx”, ignore = true)跳過(guò)指定字段。
四、性能對(duì)比與實(shí)測(cè)
| 工具 | 性能(高/中/低) | 備注 |
|---|---|---|
| 手動(dòng)拷貝 | 極高 | 無(wú)反射,代碼量大 |
| CGLIB BeanCopier | 極高 | 字節(jié)碼生成,適合高并發(fā) |
| MapStruct | 極高 | 編譯期生成,適合復(fù)雜場(chǎng)景 |
| Spring BeanUtils | 低~中 | 反射實(shí)現(xiàn),適合簡(jiǎn)單場(chǎng)景 |
| Apache BeanUtils | 低 | 反射+類型轉(zhuǎn)換,性能最差 |
| Dozer/ModelMapper | 中 | 反射+復(fù)雜映射,靈活性高 |
| JSON序列化 | 低 | 適合臨時(shí)深拷貝,性能低 |
實(shí)測(cè)建議:
- 單次拷貝或?qū)傩陨贂r(shí)性能差異不明顯,批量場(chǎng)景下優(yōu)先選MapStruct或CGLIB BeanCopier。
- MapStruct適合字段復(fù)雜、類型多樣、性能敏感場(chǎng)景。
- BeanUtils適合簡(jiǎn)單、快速開發(fā)和原型驗(yàn)證。
五、實(shí)際項(xiàng)目中的選擇建議
- 微服務(wù)、領(lǐng)域模型轉(zhuǎn)換:推薦MapStruct(如DTO→Entity、VO→DTO)。
- Spring項(xiàng)目簡(jiǎn)單Bean拷貝:用Spring BeanUtils。
- 需要深拷貝/嵌套對(duì)象:Dozer、ModelMapper或JSON序列化。
- 批量高性能場(chǎng)景:CGLIB BeanCopier或MapStruct。
- 自定義字段、類型轉(zhuǎn)換復(fù)雜:MapStruct或手動(dòng)拷貝。
六、自定義拷貝場(chǎng)景舉例
1. 只拷貝非null字段
public static void copyNonNullProperties(Object src, Object target) {
BeanWrapper srcWrap = new BeanWrapperImpl(src);
BeanWrapper trgWrap = new BeanWrapperImpl(target);
for (PropertyDescriptor pd : srcWrap.getPropertyDescriptors()) {
Object value = srcWrap.getPropertyValue(pd.getName());
if (value != null) {
trgWrap.setPropertyValue(pd.getName(), value);
}
}
}2. 字段名映射(MapStruct示例)
@Mapper
public interface MyMapper {
@Mappings({
@Mapping(source = "userName", target = "name"),
@Mapping(source = "userAge", target = "age")
})
TargetBean toTarget(SourceBean source);
}3. 嵌套對(duì)象拷貝(MapStruct示例)
@Mapper
public interface OrderMapper {
OrderDTO toDTO(Order order);
AddressDTO addressToDTO(Address address);
}MapStruct會(huì)自動(dòng)遞歸調(diào)用addressToDTO。
七、最佳實(shí)踐
- 統(tǒng)一轉(zhuǎn)換入口:在項(xiàng)目中建議統(tǒng)一封裝Bean拷貝工具類,便于維護(hù)和擴(kuò)展。
- 單元測(cè)試覆蓋:復(fù)雜轉(zhuǎn)換邏輯務(wù)必寫單元測(cè)試,防止屬性丟失或錯(cuò)誤。
- 性能評(píng)估:批量轉(zhuǎn)換場(chǎng)景建議實(shí)際測(cè)評(píng)不同工具性能。
- 異常處理:注意捕獲和處理轉(zhuǎn)換異常,避免因拷貝失敗影響業(yè)務(wù)流程。
八、參考工具封裝(示例)
public class BeanCopyUtil {
public static <T> T copy(Object source, Class<T> clazz) {
try {
T target = clazz.newInstance();
BeanUtils.copyProperties(source, target);
return target;
} catch (Exception e) {
throw new RuntimeException("Bean copy failed", e);
}
}
}可根據(jù)實(shí)際需要擴(kuò)展支持MapStruct、CGLIB等。
到此這篇關(guān)于Java Bean所有拷貝方式使用方法及性能比較詳解的文章就介紹到這了,更多相關(guān)Java Bean所有拷貝方式使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
idea使用mybatis插件mapper中的方法爆紅的解決方案
這篇文章主要介紹了idea使用mybatis插件mapper中的方法爆紅的解決方案,文中給出了詳細(xì)的原因分析和解決方案,對(duì)大家解決問(wèn)題有一定的幫助,需要的朋友可以參考下2024-07-07
基于spring+hibernate+JQuery開發(fā)之電子相冊(cè)(附源碼下載)
本篇文章介紹了,基于spring+hibernate+JQuery開發(fā)之電子相冊(cè)(附源碼下載)。需要的朋友參考下2013-05-05
elasticsearch索引index數(shù)據(jù)功能源碼示例
這篇文章主要為大家介紹了elasticsearch索引index功能源碼示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-04-04
Spring?MVC和springboot靜態(tài)資源處理問(wèn)題
這篇文章主要介紹了Spring?MVC和springboot靜態(tài)資源處理問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08
Java使用itext5實(shí)現(xiàn)生成多個(gè)PDF并合并
這篇文章主要為大家詳細(xì)介紹了Java如何使用itext5實(shí)現(xiàn)生成多個(gè)PDF并合并,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-04-04
Java實(shí)現(xiàn)mybatis批量插入數(shù)據(jù)到Oracle
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)mybatis批量插入數(shù)據(jù)到Oracle 的相關(guān)資料,需要的朋友可以參考下2016-06-06

