MapStruct for Java Bean Mapping: Features, Expressions, QualifiedByName, NullValueMappingStrategy, and Decorator
MapStruct is a compile‑time Java annotation processor that generates type‑safe mapping code, offering concise, high‑performance conversions with features such as expressions, qualifiedByName methods, null‑value strategies, and decorators, illustrated through detailed code examples and usage guidelines.
MapStruct is a Java compile‑time annotation‑processing framework that automatically generates code to map one Java bean to another, aiming to minimise manual code and runtime overhead compared with reflection‑based mappers.
Simplicity: Generates straightforward assignment statements, reducing boilerplate.
Performance: No reflection is used, so mapping is fast.
Safety: Type‑safe mapping with compile‑time error detection.
Flexibility: Supports custom conversion methods, type conversions, and mapping strategies for complex scenarios.
IDE Support: Excellent integration with code completion and error highlighting.
Expression
MapStruct allows Java expressions to be evaluated during mapping. Example:
@Mapper(componentModel = "spring")
public interface MyMapper {
@Mapping(target = "createTime", expression = "java(System.currentTimeMillis())")
Target toTarget(Source source);
}The generated implementation sets createTime to System.currentTimeMillis() :
@Component
public class MyMapperImpl implements MyMapper {
@Override
public Target toTarget(Source source) {
Target target = new Target();
target.setCreateTime(System.currentTimeMillis());
return target;
}
}qualifiedByName
When default getter/setter mapping is insufficient (e.g., converting a name to uppercase), qualifiedByName can be used:
@Mapper(componentModel = "spring")
public interface MyMapper {
@Mapping(target = "name", source = "name", qualifiedByName = "toUpperCase")
Target toTarget(Source source);
@Named("toUpperCase")
default String toUpperCase(String value) {
return value == null ? null : value.toUpperCase();
}
}The generated code calls the custom method:
@Component
public class MyMapperImpl implements MyMapper {
@Override
public Target toTarget(Source source) {
if (source == null) {
return null;
}
Target target = new Target();
target.setName(toUpperCase(source.getName()));
return target;
}
}nullValueMappingStrategy
By default, a null source results in a null target field ( NullValueMappingStrategy.RETURN_NULL ). To return default values (e.g., an empty list) instead, use NullValueMappingStrategy.RETURN_DEFAULT either on a method or the whole mapper:
@Mapper(componentModel = "spring", nullValueMappingStrategy = org.mapstruct.NullValueMappingStrategy.RETURN_DEFAULT)
public interface MyMapper {
Target toTarget(Source source);
}The implementation creates empty collections when source values are null.
Decorator
Decorators allow global post‑processing of mapper results. Define a decorator class and annotate the mapper with @DecoratedWith :
public abstract class YourMapperDecorator implements YourMapper {
private final YourMapper delegate;
public YourMapperDecorator(YourMapper delegate) { this.delegate = delegate; }
@Override
public Target toTarget(Source source) {
Target result = delegate.toTarget(source);
if (result != null && result.getField() == null) {
result.setField("");
}
return result;
}
}Mapper interface:
@Mapper
@DecoratedWith(YourMapperDecorator.class)
public interface YourMapper {
Target toTarget(Source source);
}Each call to toTarget now passes through the decorator, enabling custom handling such as defaulting empty fields.
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.