Backend Development 8 min read

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.

macrozheng
macrozheng
macrozheng
Boost Spring Apps with MapStruct: Seamless Conversion Service Integration
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.

<code>@Mapper(componentModel = "spring")
public interface AreaMapping {
    List<AreaInfoListVO> toVos(List<Area> areas);
}
</code>

The above interface converts a list of PO objects to a list of VO objects with just a few lines.

<code>// spring bean
@Autowired
AreaMapping areaMapping;

// conversion source
List<Area> areas = …;
// conversion target
List<AreaInfoListVO> vos = areaMapping.toVos(areas);
</code>

Writing such conversion manually would take a lot of time.

Converter

Spring provides a

Converter&lt;S,T&gt;

interface:

<code>@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;
        };
    }
}
</code>

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:

<code>&lt;T&gt; T convert(@Nullable Object source, Class&lt;T&gt; targetType);
</code>

MapStruct Spring Extensions

The official MapStruct Spring Extensions plugin makes any mapper that also implements

Converter

automatically register with

ConversionService

:

<code>@Mapper(componentModel = "spring")
public interface CarMapper extends Converter<Car, CarDto> {
    @Mapping(target = "seats", source = "seatConfiguration")
    CarDto convert(Car car);
}
</code>

Usage:

<code>@Autowired
private ConversionService conversionService;

Car car = …;
CarDto carDto = conversionService.convert(car, CarDto.class);
</code>

The plugin generates an adapter class that registers the mapper:

<code>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);
    }
}
</code>

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:

<code>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 {
}
</code>

Specifying a ConversionService Bean

If multiple

ConversionService

beans exist, specify which one to use via

conversionServiceBeanName

:

<code>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 {
}
</code>

Integrating Spring’s Built‑in Converters

Spring provides many useful

Converter&lt;S,T&gt;

implementations that are not directly exposed. Register them via

externalConversions

:

<code>@MapperConfig(componentModel = "spring")
@SpringMapperConfig(
   externalConversions = @ExternalConversion(sourceType = String.class, targetType = Locale.class))
public interface MapstructConfig {}
</code>

The generated adapter will contain:

<code>@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);
    }
}
</code>

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.

Javacode generationbackend developmentspringMapStructConversionService
macrozheng
Written by

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.

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.