When I first saw MapStruct, I was personally very happy. Because it happened to coincide with what I was thinking inside.

1 what is a MapStruct?

1.1 JavaBean Problems

Conversion between Javabeans in code has been something that has bothered me for a long time. When I was developing, I saw a lot of javabeans between business codes, which affected the look and feel very much, but had to exist. And one of the things THAT I thought about later was by reflection, or by writing a lot of converters myself. A complete PDF version of the Java Interview handbook has been organized into a document

The first method through reflection is indeed convenient, but now whether BeanUtils, BeanCopier, etc., the use of reflection will affect the performance. Although we can cache reflected information to improve performance. However, in this case, the type and name need to be the same to map, and there are many times when there is a lot of manual set/get and so on, due to the different nouns used by different teams.

The second is that it is time-consuming and requires method changes when adding new fields. However, since no reflection is required, the performance is very high.

1.2 Changes brought by MapStruct

MapSturct is an Annotation processor that generates type-safe, high-performance, and dependency free JavaBean mapping code.

Get to the point:

  1. Annotation processor

  2. Can generate javabeans between that mapping code

  3. Type safe, high performance, dependency free

From a literal perspective, this tool can help us implement transformations between Javabeans by way of annotations.

At the same time, as a tool class, compared with handwriting, it should be convenient and error-free.

2 MapStruct introduction

Getting started is easy. I do project JAR package management based on Maven.

2.1 Importing Dependencies

< the properties > < org. Mapstruct. Version > 1.3.0. The Final < / org. Mapstruct. Version > < / properties > < the dependency > <groupId>org.mapstruct</groupId> <artifactId>mapstruct-jdk8</artifactId> <version>${org.mapstruct.version}</version> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> </dependency>Copy the code

2.2 Creating Entity and DTO Objects

This class is taken from a github ordering system.

@data public class Order {/** * Order id */ private Long ID; /** * private String orderSn; /** * Private String receiverKeyword; /** * Order status: 0-> To be paid; 1-> Goods to be shipped; 2-> Shipped; 3-> Completed; 4-> Closed; 5-> Invalid order */ private Integer status; /** * Order type: 0-> Normal order; 1-> order */ private Integer orderType; /** * order source: 0->PC order; 1->app order */ private Integer sourceType; }Copy the code

Corresponding query parameters

@data public class OrderQueryParam {/** * private String orderSn; /** * Private String receiverKeyword; /** * Order status: 0-> To be paid; 1-> Goods to be shipped; 2-> Shipped; 3-> Completed; 4-> Closed; 5-> Invalid order */ private Integer status; /** * Order type: 0-> Normal order; 1-> order */ private Integer orderType; /** * order source: 0->PC order; 1->app order */ private Integer sourceType; }Copy the code

2.3 write Mapper

Mapper is a Mapper, generally speaking, is to write xxxMapper interface. Of course, it doesn’t have to end in Mapper. Only that’s what the authorities say. In this example, the corresponding interfaces are as follows

import com.homejim.mapstruct.dto.OrderQueryParam;
import com.homejim.mapstruct.entity.Order;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

@Mapper
public interface OrderMapper {

    OrderQueryParam entity2queryParam(Order order);

}
Copy the code

Simple mapping (field and type match) with only one requirement, @mapper annotation on interface. The input parameter corresponds to the object to be converted, the return value corresponds to the converted object, and the method name can be arbitrary.

2.4 test

Write a test class to test it.

@Test
public void entity2queryParam() {
    Order order = new Order();
    order.setId(12345L);
    order.setOrderSn("orderSn");
    order.setOrderType(0);
    order.setReceiverKeyword("keyword");
    order.setSourceType(1);
    order.setStatus(2);

    OrderMapper mapper = Mappers.getMapper(OrderMapper.class);
    OrderQueryParam orderQueryParam = mapper.entity2queryParam(order);
    assertEquals(orderQueryParam.getOrderSn(), order.getOrderSn());
    assertEquals(orderQueryParam.getOrderType(), order.getOrderType());
    assertEquals(orderQueryParam.getReceiverKeyword(), order.getReceiverKeyword());
    assertEquals(orderQueryParam.getSourceType(), order.getSourceType());
    assertEquals(orderQueryParam.getStatus(), order.getStatus());

}
Copy the code

The test passed without any problems.

3 MapStruct analysis

Above, I wrote 3 steps to implement the transformation from Order to OrderQueryParam.

So, as an annotation processor, what are the advantages of MapStruct generated code?

3.1 the high performance

This is in contrast to reflection, which requires reading bytecode content, which can be expensive. The code generated by MapStruct is similar to writing by hand. Speed can be guaranteed.

The code generated in the previous example can be seen after compilation. Annotations see this in target/generated-sources/ Annotations.

Generated code

Corresponding code

@Generated( value = "org.mapstruct.ap.MappingProcessor", date = "2019-08-02T00:29:49+0800", comments = "version: 1.3.0. Final, the compiler: javac, environment: Java 11.0.2 (Oracle Corporation)") public class implements OrderMapperImpl {@override public OrderQueryParam entity2queryParam(Order order) { if ( order == null ) { return null; } OrderQueryParam orderQueryParam = new OrderQueryParam(); orderQueryParam.setOrderSn( order.getOrderSn() ); orderQueryParam.setReceiverKeyword( order.getReceiverKeyword() ); orderQueryParam.setStatus( order.getStatus() ); orderQueryParam.setOrderType( order.getOrderType() ); orderQueryParam.setSourceType( order.getSourceType() ); return orderQueryParam; }}Copy the code

You can see that it generates an implementation class, and the code is as simple as writing it by hand.

3.2 easy to debug

In our generated code, we can easily debug it.

Easy to DEBUG

When using reflection, if something goes wrong, it is often difficult to find the cause.

3.3 Relatively simple to use

If it’s fully mapped, it’s definitely not as easy to use as reflection. With tools like BeanUtils it’s all done in one statement. However, if special matching is required (special type conversions, many-to-one conversions, and so on), it is also relatively simple. A complete PDF version of the Java Interview handbook has been organized into a document

Basically, when using, we just need to declare an interface, write corresponding methods under the interface, can be used. Of course, if there are special circumstances, there is a need for additional treatment.

3.4 Code Independence

The generated code is oppositional and has no run-time dependencies.