Boost Spring Apps with MapStruct: Seamless Conversion Service Integration
This article explains how to replace manual BeanUtil mapping with MapStruct, leverage Spring's Converter interface, and use the MapStruct Spring Extensions plugin to automatically register mappers with ConversionService, including custom adapter configuration and integration of built‑in Spring converters.
Previously I recommended the MapStruct tool, which can replace BeanUtil for converting between DTO, VO, and PO objects using Java's compile‑time annotation processor.
MapStruct generates clean code, dramatically reducing manual mapping effort.
@Mapper(componentModel = "spring")
public interface AreaMapping {
List<AreaInfoListVO> toVos(List<Area> areas);
}The above interface converts a list of PO objects to a list of VO objects with just a few lines.
// spring bean
@Autowired
AreaMapping areaMapping;
// conversion source
List<Area> areas = …;
// conversion target
List<AreaInfoListVO> vos = areaMapping.toVos(areas);Writing such conversion manually would take a lot of time.
Converter
Spring provides a Converter<S,T> interface:
@FunctionalInterface
public interface Converter<S,T> {
@Nullable
T convert(S source);
default <U> Converter<S, U> andThen(Converter<? super T, ? extends U> after) {
Assert.notNull(after, "After Converter must not be null");
return (s) -> {
T initialResult = this.convert(s);
return initialResult != null ? after.convert(initialResult) : null;
};
}
}It converts a source S to a target T, which aligns with MapStruct’s purpose.
Converters are registered to ConversionService via ConverterRegistry, allowing you to invoke:
<T> T convert(@Nullable Object source, Class<T> targetType);MapStruct Spring Extensions
The official MapStruct Spring Extensions plugin makes any mapper that also implements Converter automatically register with ConversionService:
@Mapper(componentModel = "spring")
public interface CarMapper extends Converter<Car, CarDto> {
@Mapping(target = "seats", source = "seatConfiguration")
CarDto convert(Car car);
}Usage:
@Autowired
private ConversionService conversionService;
Car car = …;
CarDto carDto = conversionService.convert(car, CarDto.class);The plugin generates an adapter class that registers the mapper:
package org.mapstruct.extensions.spring.converter;
import cn.felord.mapstruct.entity.Car;
import cn.felord.mapstruct.entity.CarDto;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.convert.ConversionService;
import org.springframework.stereotype.Component;
@Component
public class ConversionServiceAdapter {
private final ConversionService conversionService;
public ConversionServiceAdapter(@Lazy final ConversionService conversionService) {
this.conversionService = conversionService;
}
public CarDto mapCarToCarDto(final Car source) {
return (CarDto) this.conversionService.convert(source, CarDto.class);
}
}Custom Adapter Package and Name
By default the generated adapter resides in org.mapstruct.extensions.spring.converter with the name ConversionServiceAdapter. You can change the package and class name:
package cn.felord.mapstruct.config;
import org.mapstruct.MapperConfig;
import org.mapstruct.extensions.spring.SpringMapperConfig;
@MapperConfig(componentModel = "spring")
@SpringMapperConfig(conversionServiceAdapterPackage = "cn.felord.mapstruct.config",
conversionServiceAdapterClassName = "MapStructConversionServiceAdapter")
public class MapperSpringConfig {
}Specifying a ConversionService Bean
If multiple ConversionService beans exist, specify which one to use via conversionServiceBeanName:
package cn.felord.mapstruct.config;
import org.mapstruct.MapperConfig;
import org.mapstruct.extensions.spring.SpringMapperConfig;
@MapperConfig(componentModel = "spring")
@SpringMapperConfig(conversionServiceAdapterPackage = "cn.felord.mapstruct.config",
conversionServiceAdapterClassName = "MapStructConversionServiceAdapter",
conversionServiceBeanName = "myConversionService")
public class MapperSpringConfig {
}Integrating Spring’s Built‑in Converters
Spring provides many useful Converter<S,T> implementations that are not directly exposed. Register them via externalConversions:
@MapperConfig(componentModel = "spring")
@SpringMapperConfig(
externalConversions = @ExternalConversion(sourceType = String.class, targetType = Locale.class))
public interface MapstructConfig {}The generated adapter will contain:
@Component
public class ConversionServiceAdapter {
private final ConversionService conversionService;
public ConversionServiceAdapter(@Lazy final ConversionService conversionService) {
this.conversionService = conversionService;
}
public Locale mapStringToLocale(final String source) {
return conversionService.convert(source, Locale.class);
}
}Summary
mapstruct-spring-annotations enables developers to use ConversionService with defined MapStruct mappers without importing each mapper individually, allowing loose coupling between mappers while preserving MapStruct’s core mechanisms.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
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.
