! [](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/09ff17adc04b4186b1779c40e7bbf06e~tplv-k3u1fbpfcp-zoom-1.image)

This article will take a brief look at the use of MapStruct and compare it with several other utility classes.

Why do WE need MapStruct?

First, let’s talk about what kind of scenarios MapStruct is suitable for and why there are so many similar frameworks on the market.

In software architecture design, hierarchical structure is the most common and important one. Many people are familiar with three-tier architectures, four-tier architectures, and so on.

It has even been said that “any problem in computer science can be solved by adding an indirect intermediate layer. If not, add two layers.”

However, as the software architecture becomes more and more layered, the data model between each layer is faced with the problem of conversion. Typically, we can see various O’s in the code, such as DO, DTO, VO, etc.

In general, we use different data models at different levels for the same data model. For example, in the data store layer, we use DO to abstract a business entity; At the business logic layer, we use Dtos to represent data transfer objects. At the presentation layer, we encapsulate the object as VO to interact with the front end.

Therefore, data from the front end to the data persistence layer (from the persistence layer to the front end) transparently needs to be transformed between objects, that is, mapping between different object models.

Usually, we can use get/set to map fields one by one, for example:

personDTO.setName(personDO.getName());

personDTO.setAge(personDO.getAge());

personDTO.setSex(personDO.getSex());

personDTO.setBirthday(personDO.getBirthday());

However, writing such mapping code is a lengthy and error-prone task. The goal of frameworks like MapStruct is to make this as easy as possible by automating it.

The use of MapStruct

MapStruct (mapstruct.org/) is a code generator that greatly simplifies the implementation of mappings between Java bean types based on the “convention over configuration” approach. The generated mapping code uses pure method calls, making it fast, type-safe, and easy to understand.

Convention over configuration, also known as programming by convention, is a software design paradigm that aims to reduce the number of decisions software developers have to make and gain the benefits of simplicity without losing flexibility.

Suppose we have two classes that need to be converted to each other, PersonDO and PersonDTO, with the following class definitions:

publicclassPersonDO {

privateInteger id;

privateStringname;

privateintage;

privateDatebirthday;

privateStringgender;

}

publicclassPersonDTO {

privateStringuserName;

privateInteger age;

privateDatebirthday;

privateGender gender;

}

Let’s demonstrate how to map beans using MapStruct.

To use MapStruct, you first need to rely on its associated jar package. Using Maven, you need to rely on it as follows:

.

1.3.1. The Final

.

org.mapstruct

mapstruct

${org.mapstruct.version}

.

org.apache.maven.plugins

maven-compiler-plugin

3.8.1

1.8

1.8

org.mapstruct

mapstruct-processor

${org.mapstruct.version}

Because MapStruct needs to generate conversion code in the compiler, a reference to mapstruct-Processor needs to be configured in the Maven-Compiler-plugin plug-in. This part will be covered later.

After that, we need to define an interface to do the mapping, the main code is as follows:

@Mapper

interfacePersonConverter{

PersonConverter INSTANCE = Mappers.getMapper(PersonConverter.class);

@Mappings(@Mapping(source =”name”, target =”userName”))

PersonDTOdo2dto(PersonDO person);

}

Using the @mapper annotation, define a Converter interface in which you define a do2dTO method whose input parameter type is PersonDO and output parameter type is PersonDTO, which is used to convert PersonDO to PersonDTO.

The test code is as follows:

publicstaticvoidmain(String[] args){

PersonDO personDO =newPersonDO();

personDO.setName(“Hollis”);

personDO.setAge(26);

personDO.setBirthday(newDate());

personDO.setId(1);

personDO.setGender(Gender.MALE.name());

PersonDTO personDTO = PersonConverter.INSTANCE.do2dto(personDO);

System.out.println(personDTO);

}

Output result:

PersonDTO{userName=’Hollis’, age=26, birthday=Sat Aug 08 19:00:44 CST 2020, gender=MALE}

As you can see, we used MapStruct to perfectly convert PersonDO to PersonDTO.

As you can see from the above code, MapStruct is relatively simple to use, relying mainly on the @mapper annotation.

But we know that most of the time, the attribute names, types, and so on between the two classes we need to convert to each other are not exactly the same, and there are also cases where we don’t want to map directly, so what do we do?

And MapStruct actually does that very well.

MapStruct handles field mappings

First of all, we can explicitly tell you that if the type and name of the source object attribute in the two classes to be converted are the same as the target object attribute, the corresponding attribute will be mapped automatically.

So, how to deal with special cases?

How to map inconsistent names

In the example above, name is used to represent the userName in PersonDO and userName is used to represent the userName in PersonDTO.

You can use the @mapping annotation on the method signature and specify the name of the source object and the name of the target object. For example, you can map the value of name to userName as follows:

@Mapping(source=”name”, target =”userName”)

Types that can be mapped automatically

In addition to inconsistent names, there is a special case where the type is inconsistent. In the example above, the user gender is represented by a String type in PersonDO and an enumeration of Genter in PersonDTO.

When the type is inconsistent, it is necessary to convert each other

In fact, MapStruct automatically maps some types, so we don’t need to do any extra configuration. For example, we converted String to enumeration.

In general, automatic type conversions can be done for:

Basic types and their corresponding packaging types.

Between the wrapper type of the base type and the String type

Between String and enumeration types

Custom constant

If we want to define a fixed value for some attributes during transformation mapping, we can use constant

@Mapping(source=”name”, constant =”hollis”)

How to map inconsistent types

Again, if we need to add the HomeAddress property to the Person object, we typically define a separate HomeAddress class in PersonoDTO to represent the HomeAddress, whereas in the Person class we typically use a String to represent the HomeAddress.

This requires converting between HomeAddress and String using JSON, and MapStruct also supports this.

publicclassPersonDO{

privateString name;

privateString address;

}

publicclassPersonDTO{

privateString userName;

privateHomeAddress address;

}

@Mapper

interfacePersonConverter{

PersonConverter INSTANCE = Mappers.getMapper(PersonConverter.class);

@Mapping(source =”userName”, target =”name”)

@Mapping(target =”address”,expression =”java(homeAddressToString(dto2do.getAddress()))”)

PersonDO dto2do(PersonDTO dto2do);

defaultString homeAddressToString(HomeAddress address){

returnJSON.toJSONString(address);

}

}

We only need to define a method in PersonConverter (since PersonConverter is an interface, we can define a default method in JDK 1.8 or later) that converts HomeAddress to String.

Default method: a new language feature introduced in Java 8, annotated with the keyword default. Methods annotated by default need to provide an implementation, and subclasses can choose to implement or not implement the method

Then, on the dto2DO method, you can convert the type by using the following annotations:

@Mapping(target=”address”,expression =”java(homeAddressToString(dto2do.getAddress()))”)

There are also some types of conversion that MapStruct itself supports, such as the conversion between String and Date:

@Mapping(target=”birthday”,dateFormat =”yyyy-MM-dd HH:mm:ss”)

Above, a brief introduction to some commonly used field mapping methods, but also my own work often encountered several scenarios, more cases you can see the official example (github.com/mapstruct/m…

The performance of the MapStruct

MapStruct: MapStruct: MapStruct: MapStruct: MapStruct: MapStruct: MapStruct: MapStruct: MapStruct: MapStruct: MapStruct

See “Why Does Alibaba prohibit copy of attributes using Apache Beanutils?” In this example, we perform a performance test on MapStruct.

It takes 0ms, 1ms, 3ms, and 6ms to perform 1000, 10000, 100000, and 1000000 mappings respectively.

As you can see, MapStruct’s time is very short compared to the other tools.

So why does MapStruct perform so well?

One of the biggest differences between MapStruct and other mapping frameworks is that MapStruct generates bean maps at compile time, which ensures high performance, allows problems to be reported ahead of time, and allows developers to thoroughly check for errors.

Remember when we introduced MapStruct dependencies, specifically maven-compiler-plugin support for mapstruct-Processor?

And we use many of the annotations provided by MapStruct in our code, which allows MapStruct to directly generate the bean mapping code at compile time, essentially writing a lot of setters and getters instead of us.

For example, we define the following Mapper in our code:

@Mapper

interfacePersonConverter{

PersonConverter INSTANCE = Mappers.getMapper(PersonConverter.class);

@Mapping(source =”userName”, target =”name”)

@Mapping(target =”address”,expression =”java(homeAddressToString(dto2do.getAddress()))”)

@Mapping(target =”birthday”,dateFormat =”yyyy-MM-dd HH:mm:ss”)

PersonDO dto2do(PersonDTO dto2do);

defaultString homeAddressToString(HomeAddress address){

returnJSON.toJSONString(address);

}

}

After the code is compiled, a PersonConverterImpl is automatically generated:

@Generated(

value =”org.mapstruct.ap.MappingProcessor”,

date =”2020-08-09T12:58:41+0800″,

Comments =” Version: 1.3.1.Final, Compiler: Javac, Environment: Java 1.8.0_181 (Oracle Corporation)”

)

classPersonConverterImplimplementsPersonConverter{

@Override

publicPersonDOdto2do(PersonDTO dto2do){

if( dto2do ==null) {

returnnull;

}

PersonDO personDO =newPersonDO();

personDO.setName( dto2do.getUserName() );

if( dto2do.getAge() ! =null) {

personDO.setAge( dto2do.getAge() );

}

if( dto2do.getGender() ! =null) {

personDO.setGender( dto2do.getGender().name() );

}

personDO.setAddress( homeAddressToString(dto2do.getAddress()) );

returnpersonDO;

}

}

At runtime, when the bean is mapped, the Dto2DO method of PersonConverterImpl is called directly, so there is nothing special to do but set and GET in memory.

So, MapStruct performs well at runtime because it does a lot of work at compile time, and has the added benefit of being able to expose problems earlier than at compile time.

If the code has a field mapping problem, the application will not compile, forcing the developer to fix the problem.

conclusion

This article introduces a Java field mapping tool class, MapStruct, his usage is relatively simple, and the function is very perfect, can cope with all kinds of field mapping.

And because it generates real mapping code at compile time, runtime performance is greatly improved.

Highly recommended, really delicious!!