M4RKYU.SYSEdition 2027
Skip to content
LOCEN/Ontario · CA/▸logs · advanced java simplifying object property copy and manipulation with beanutil 3l2nStandbyOK/--:--:--EST
M4M4RK_YUportfolio
  • BuildBuild
    BuildOverview
    • WorkSelected case studies and write-ups
    • GamesPlayable prototypes and game-dev logs
  • GalleryGallery
    GalleryOverview
    • PhotosPhoto collections and visual experiments
    • ShopPrints, posters, and one-off objects
  • WritingWriting
    WritingOverview
    • BlogLong-form devlogs and field notes
    • NotesShort observations, links, snippets
  • ResourcesResources
    ResourcesOverview
    • Tools38 in-browser developer utilities
    • LinksDaily-use dev and design bookmarks
  • AboutAbout
  • ContactContact
中文

syndicated · dev.to / @markyu

Java BeanUtils Copying: Convenient, but Not Free

A practical Java guide to BeanUtils, shallow copy pitfalls, reflection overhead, and when MapStruct or manual mapping is a better choice.

Published
May 26 '24
·
Reading time
3 min read
·
Reactions
12
·
Comments
2
javabackendperformancespring
View on dev.toDiscuss

On this page

  • The Basic Copy
  • Shallow Copy Is the First Trap
  • Reflection Has a Cost
  • Spring BeanUtils vs Apache BeanUtils
  • A Safer DTO Mapping Example
  • Where BeanUtils Still Makes Sense
  • Production Pitfall: Copying Too Much
  • Final Thought

Bean copying looks harmless until it hides a production bug.

I have used BeanUtils.copyProperties() for DTO conversion because it is fast to write. Then later someone adds a nested object, a field name changes, or performance matters, and the convenient helper suddenly becomes a quiet source of confusion.

So here is the practical rule:

Use BeanUtils for simple, boring object copying. Do not use it as your default domain mapping strategy.

The Basic Copy

With Apache Commons BeanUtils:

import org.apache.commons.beanutils.BeanUtils;

public class CopyDemo {
    public static void main(String[] args) throws Exception {
        UserEntity entity = new UserEntity("Mark", 30);
        UserDTO dto = new UserDTO();

        BeanUtils.copyProperties(dto, entity);

        System.out.println(dto);
    }
}

That is the reason people like it. One line replaces a bunch of setters.

But the hidden question is:

What exactly did it copy?

Shallow Copy Is the First Trap

If the object contains nested objects, BeanUtils copies the reference. It does not magically deep-copy your object graph.

class UserEntity {
    private String name;
    private Address address;
}

class UserDTO {
    private String name;
    private Address address;
}

After copying, both objects can point at the same Address.

That may be fine. It may also be a bug if one layer mutates the nested object.

Visual map:

UserEntity.address ----+
                       +----> same Address object
UserDTO.address -------+

If you need independent nested objects, write that mapping explicitly or use a mapper designed for it.

Reflection Has a Cost

BeanUtils usually relies on reflection and runtime property inspection.

That means:

  • less compile-time safety
  • slower mapping than direct code
  • mistakes may show up later
  • refactoring fields can be riskier

For one admin endpoint, who cares.

For mapping thousands of rows in a hot path, I would care.

Spring BeanUtils vs Apache BeanUtils

The method names look similar, but behavior differs.

ToolGood forWatch out
Spring BeanUtilssimple same-name copylimited conversion
Apache BeanUtilsstring conversion and dynamic property accessreflection overhead
MapStructproduction DTO mappingsetup and annotations
Manual mappingcritical domain logicmore code

My current preference:

  • quick internal tools: BeanUtils
  • API DTO mapping: MapStruct or manual mapping
  • sensitive business logic: manual mapping

A Safer DTO Mapping Example

Sometimes boring explicit code is better:

public UserDTO toDto(UserEntity entity) {
    UserDTO dto = new UserDTO();
    dto.setName(entity.getName());
    dto.setAge(entity.getAge());

    if (entity.getAddress() != null) {
        AddressDTO address = new AddressDTO();
        address.setCity(entity.getAddress().getCity());
        address.setCountry(entity.getAddress().getCountry());
        dto.setAddress(address);
    }

    return dto;
}

Yes, it is longer.

But during a code review, nobody has to guess what happens to address.

Where BeanUtils Still Makes Sense

I would still use it for:

  • small admin tools
  • test fixtures
  • simple JavaBean-to-JavaBean copying
  • prototypes
  • non-critical internal scripts

I would avoid it for:

  • hot paths
  • payment/order domain mapping
  • complex nested DTOs
  • security-sensitive field filtering
  • APIs where fields change often

Production Pitfall: Copying Too Much

This bug is easy to miss:

BeanUtils.copyProperties(userEntity, requestDto);

If requestDto contains fields like role, status, or createdAt, you may accidentally let user input overwrite fields it should not control.

For request-to-entity updates, I prefer explicit allow-list mapping:

user.setDisplayName(request.getDisplayName());
user.setBio(request.getBio());

It is less clever. It is safer.

Final Thought

BeanUtils is a convenience tool, not an architecture.

Use it where the mapping is boring. When the mapping carries business meaning, write it so the next developer can see every decision.

Have you ever had a DTO copy bug caused by a field you did not mean to map?

Related reading

Java final, finally, finalize: Three Bugs They Prevent

A practical Java explanation of final, finally, and finalize using real failure modes instead of memorized definitions.

java

Java throw vs throws: The Exception Bug They Reveal

A practical Java exception handling guide explaining throw, throws, checked exceptions, runtime exceptions, and API boundary decisions.

java

Debug a Slow MySQL Query Before You Guess at Indexes

A practical MySQL workflow for finding slow queries, reading EXPLAIN output, and deciding whether an index actually helps.

mysql

originally published

This post first ran on dev.to. Comments and reactions live there.

Continue on dev.to
PreviousRedisJSON Is Useful When You Update Parts of a DocumentA practical RedisJSON walkthrough: when to use it, when not to, and the commands that actually matter.
Back to all posts
NextNetwork Address Calculation: The Subnet Math That MattersA practical subnetting guide showing how to calculate a network address from an IP address and mask using binary math and simple examples.
Back to archive
M4RKYUM4RKYUM4RKYUM4RKYUM4RKYUM4RKYUM4RKYUM4RKYU
Crafted since 2024
ZhenXiao Mark YuZhenXiao Mark Yu
get in touch

Saw something here?Tell me about it.

It's a portfolio, not a service · but I read every note — drop a line if anything here resonated, or just to say hi.

Start a conversation
open channel

say hi anytime · 2026

--:--:--ESTOntario, Canada
  • Email
  • GitHub
  • dev.to
  • LinkedIn
  • Twitter / X
  • Instagram
  • Facebook
  • YouTube
  • CodePen
  • Spotify
  • Snapchat

Newsletter

Get the occasional dispatch

Notes and logs from m4rkyu.com — short, dated, no noise. Unsubscribe anytime.

Work

Production builds, games, and visual archives.

  • Projects
  • Games
  • Archive
  • Logs

Resources

Daily-use tools and a personal link library.

  • Search
  • Latest
  • Tools
  • Links
  • Notes
  • Topics
  • Shop
RSSJSON feed

Studio

Background, contact, and channels for collaboration.

  • About
  • Contact
  • Changelog
  • Colophon
  • Resumepending

Socials

Find me on the usual feeds.

  • GitHub
  • dev.to
  • LinkedIn
  • Twitter / X
  • Instagram
  • Facebook
  • YouTube
  • CodePen
  • Spotify
  • Snapchat
  • Email
© 2026 ZhenXiao Mark Yumarkyu0615@gmail.com
  • Email
  • GitHub
  • dev.to
  • LinkedIn
  • Twitter / X
  • Instagram
  • Facebook
  • YouTube
  • CodePen
  • Spotify
  • Snapchat
PrivacyTermsBuilt with Next.js 16 · React 19 · Tailwind 4