Java Object Copy Tools Compared: BeanUtils, BeanCopier, Hutool, MapStruct & Dozer

This article reviews common Java bean‑copy utilities—including Apache BeanUtils, Spring BeanUtils, cglib BeanCopier, Hutool BeanUtil, MapStruct, and Dozer—explaining their dependencies, usage examples, shallow versus deep copy behavior, and how to choose the right tool for backend development.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
Java Object Copy Tools Compared: BeanUtils, BeanCopier, Hutool, MapStruct & Dozer

In daily development we often need to copy or convert objects such as DTO, PO, VO, DO, TO, BO, etc., and the frequency of these operations grows with business complexity.

Common Bean Copy Tools

Apache BeanUtils

Spring BeanUtils

cglib BeanCopier

Hutool BeanUtil

MapStruct

Dozer

1. Apache BeanUtils

Dependency:

<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.3</version>
</dependency>

Typical test code:

@org.junit.Test
public void test() {
    OrderPO orderPO = new OrderPO();
    orderPO.setId(1);
    orderPO.setOrderNumber("orderNumber");
    ArrayList<String> list = new ArrayList<String>() {{
        add("1");
        add("2");
    }};
    orderPO.setProId(list);

    OrderDTO orderDTO = new OrderDTO();
    BeanUtils.copyProperties(orderDTO, orderPO);
}

Result (shallow copy):

OrderPO(id=1, orderNumber=orderNumber, proId=[1, 2])
OrderDTO(id=1, orderNumber=orderNumber, proId=[1, 2])

When the list reference is modified, the target object sees the change, confirming a shallow copy:

list.add("3");
log.info(orderDTO.getProId());
OrderDTO(id=1, orderNumber=orderNumber, proId=[1, 2, 3])

2. Spring BeanUtils

Dependency (already present in most Spring projects):

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.2.2.RELEASE</version>
</dependency>

Usage (parameter order is source, target):

BeanUtils.copyProperties(orderPO, orderDTO);
BeanUtils.copyProperties(orderPO, orderDTO, "orderNumber");

Result with ignored property:

OrderPO(id=1, orderNumber=orderNumber, proId=[1, 2])
OrderDTO(id=1, orderNumber=null, proId=[1, 2])

3. cglib BeanCopier

Dependency:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

Usage:

BeanCopier beanCopier = BeanCopier.create(
        orderPO.getClass(),
        orderDTO.getClass(), false);
beanCopier.copy(orderPO, orderDTO, null);

Result (shallow copy, primitive vs wrapper issue):

OrderPO(id=1, orderNumber=orderNumber, proId=[1, 2])
OrderDTO(id=0, orderNumber=orderNumber, proId=[1, 2])

4. Hutool BeanUtil

Dependency:

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.1.0</version>
</dependency>

Usage (also shallow copy):

BeanUtil.copyProperties(orderPO, orderDTO);

Hutool provides many additional utility methods and supports ignore‑properties in the same way as Spring BeanUtils.

5. MapStruct

MapStruct generates mapping code at compile time, avoiding reflection and offering high performance.

Dependencies:

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-jdk8</artifactId>
    <version>1.3.0.Final</version>
</dependency>
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.3.0.Final</version>
</dependency>

Mapper interface:

@Mapper
public interface ConvertMapper {
    OrderDTO po2Dto(OrderPO orderPO);
}

Usage:

ConvertMapper mapper = Mappers.getMapper(ConvertMapper.class);
OrderDTO orderDTO = mapper.po2Dto(orderPO);

The generated implementation performs deep copy for reference fields, creating new objects instead of sharing references.

6. Dozer

Dozer maps beans recursively and can handle different field names via XML configuration.

Dependency:

<dependency>
    <groupId>net.sf.dozer</groupId>
    <artifactId>dozer</artifactId>
    <version>5.4.0</version>
</dependency>

Usage:

DozerBeanMapper mapper = new DozerBeanMapper();
OrderDTO orderDTO = mapper.map(orderPO, OrderDTO.class);

XML mapping for fields with different names:

<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://dozer.sourceforge.net
          http://dozer.sourceforge.net/schema/beanmapping.xsd">
    <mapping>
        <class-a>com.cn.entity.OrderPO</class-a>
        <class-b>com.cn.entity.OrderDTO</class-b>
        <field>
            <a>name</a>
            <b>value</b>
        </field>
    </mapping>
</mappings>

Result after applying the mapping:

OrderPO(id=1, orderNumber=orderNumber, proId=[1, 2], name=hydra)
OrderDTO(id=1, orderNumber=orderNumber, proId=[1, 2], value=hydra)

Choosing the appropriate tool depends on whether a shallow or deep copy is required, performance considerations, and existing project dependencies.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javaspringmapstructObject Copybean-mappingDozer
Su San Talks Tech
Written by

Su San Talks Tech

Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.