Backend Development 7 min read

Custom BeanConvertUtils: Simplifying PO/VO/DTO Conversion in Java

This article introduces a custom BeanConvertUtils class that extends BeanUtils to provide concise, lambda‑friendly conversion of single objects and lists between PO, VO, and DTO types, while highlighting BeanUtils limitations, performance impact, and important usage cautions.

Java Architect Essentials
Java Architect Essentials
Java Architect Essentials
Custom BeanConvertUtils: Simplifying PO/VO/DTO Conversion in Java

BeanUtils.copyProperties() is convenient for copying properties between PO, VO, and DTO objects, but it cannot copy lists, often requires repetitive code, and lacks a return value, making it unfriendly for lambda expressions.

Key shortcomings:

Cannot copy List objects, leading to boilerplate loops.

Simple VO conversions still need explicit new Vo() calls.

Absence of a return value hampers stream and lambda usage.

To address these issues, a new utility class BeanConvertUtils is introduced. It provides static methods convertTo and convertListTo that accept a source object (or list), a Supplier for the target type, and an optional callback for custom field handling.

Usage example for a single object:

// before
public Vo findById(Integer id) {
    Vo vo = new Vo();
    Po po = dao.findById(id);
    copyProperties(po, vo);
    return vo;
}

// after
public Vo findById(Integer id) {
    return BeanConvertUtils.convertTo(dao.findById(id), Vo::new);
}

// after with custom field handling
public Vo findById(Integer id) {
    return BeanConvertUtils.convertTo(dao.findById(id), Vo::new,
        (s, t) -> t.setName(s.getName));
}

Usage example for a list:

// before
public List
findAll() {
    List
vos = new ArrayList();
    List
pos = dao.findAll();
    for (Po po : pos) {
        Vo vo = new Vo();
        BeanUtils.copyProperties(po, vo);
        vos.add(vo);
    }
    return vos;
}

// after
public List
findAll() {
    return BeanConvertUtils.convertListTo(dao.findAll(), Vo::new);
}

// after with custom field handling
public List
findAll() {
    return BeanConvertUtils.convertListTo(dao.findAll(), Vo::new,
        (s, t) -> t.setName(s.getName));
}

The full implementation of BeanConvertUtils extends BeanUtils and includes overloaded methods for conversion with optional callbacks, as well as a functional interface ConvertCallBack for custom processing.

/**
 * Conversion utility
 */
public class BeanConvertUtils extends BeanUtils {

    public static
T convertTo(S source, Supplier
targetSupplier) {
        return convertTo(source, targetSupplier, null);
    }

    /**
     * Convert object with optional callback
     */
    public static
T convertTo(S source, Supplier
targetSupplier, ConvertCallBack
callBack) {
        if (source == null || targetSupplier == null) {
            return null;
        }
        T target = targetSupplier.get();
        copyProperties(source, target);
        if (callBack != null) {
            callBack.callBack(source, target);
        }
        return target;
    }

    public static
List
convertListTo(List
sources, Supplier
targetSupplier) {
        return convertListTo(sources, targetSupplier, null);
    }

    /**
     * Convert list with optional callback
     */
    public static
List
convertListTo(List
sources, Supplier
targetSupplier, ConvertCallBack
callBack) {
        if (sources == null || targetSupplier == null) {
            return null;
        }
        List
list = new ArrayList<>(sources.size());
        for (S source : sources) {
            T target = targetSupplier.get();
            copyProperties(source, target);
            if (callBack != null) {
                callBack.callBack(source, target);
            }
            list.add(target);
        }
        return list;
    }

    @FunctionalInterface
    public interface ConvertCallBack
{
        void callBack(S s, T t);
    }
}

Performance: Since the utility merely wraps BeanUtils, its runtime overhead is negligible, with the extra method call stack being the only difference.

Reminders:

The utility does not solve deep‑copy problems; BeanUtils itself has limitations in that area.

If either the source or the Supplier is null , the methods return null instead of throwing an exception, delegating null‑handling responsibility to the caller.

JavaPerformanceBackend DevelopmentUtilityBeanUtilsObject Mapping
Java Architect Essentials
Written by

Java Architect Essentials

Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.

0 followers
Reader feedback

How this landed with the community

login 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.