Advanced MapStruct Usage: Expressions, qualifiedByName, NullValueMappingStrategy, and Decorator
This article introduces MapStruct, a compile‑time Java bean‑mapping framework, and demonstrates advanced features such as expression mapping, qualifiedByName for custom conversions, null‑value handling strategies, and the use of decorators to extend mapper behavior, complete with code examples.
MapStruct is a compile‑time annotation‑processing framework for Java that automatically generates type‑safe mapping code between beans, aiming for minimal code, high performance, and strong IDE support.
Key characteristics include concise mapping definitions, excellent runtime performance because no reflection is used, compile‑time type safety, flexibility through custom conversion methods, and good integration with IDEs.
Expression Mapping
MapStruct allows the use of Java expressions to set target fields 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, qualifiedByName can invoke a custom method, such as converting a string to uppercase:
@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, MapStruct returns null for target fields when the source is null . To provide default values (e.g., an empty list), the NullValueMappingStrategy.RETURN_DEFAULT can be applied at method or class level.
Manual handling example:
@Component
public class MyMapperImpl implements MyMapper {
@Override
public Target toTarget(Source source) {
if (source == null) {
return null;
}
Target target = new Target();
target.setName(source.getName());
List
list = source.getIds();
if (list != null) {
target.setIds(new ArrayList
(list));
} else {
target.setIds(null);
}
return target;
}
}Using the strategy:
@Mapper(componentModel = "spring",
nullValueMappingStrategy = org.mapstruct.NullValueMappingStrategy.RETURN_DEFAULT)
public interface MyMapper {
Target toTarget(Source source);
} @Component
public class MyMapperImpl implements MyMapper {
@Override
public Target toTarget(Source source) {
Target target = new Target();
if (source != null) {
target.setName(toUpperCase(source.getName()));
List
list = source.getIds();
if (list != null) {
target.setIds(new ArrayList
(list));
} else {
target.setIds(new ArrayList
());
}
}
return target;
}
}Decorator
A decorator class can wrap a generated mapper to apply global post‑processing logic:
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) {
if (result.getField() == null) {
result.setField("");
}
// additional field handling can be added here
}
return result;
}
}Apply the decorator with @DecoratedWith on the mapper interface:
@Mapper
@DecoratedWith(YourMapperDecorator.class)
public interface YourMapper {
Target toTarget(Source source);
}With this setup, every call to toTarget passes through the decorator, allowing custom default values or other cross‑cutting concerns to be handled centrally.
Overall, the article provides practical, advanced MapStruct techniques that help developers write cleaner, safer, and more maintainable bean‑mapping code.
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.