BeanCopy framework Ultimate Guide

Foreword

image.png

As shown, under development, both MVC-style three-tier structure, or DDD field-driven architecture. There will always be demand for all kinds of conversion between the DTO, DO, PO, VO. Therefore, we define two often Object fields are consistent, easy to operate coating Assember. But the reality demand will encounter some complex mappings. So how should we choose the right BeanCopy scenario-based framework it?

This blog is mainly tidy BeanCopy class framework.

  • Each frame performance
  • how to choose

BeanCopy framework

In addition to HardCopy (handwritten set get) commonly used BeanCopy choose the following:

I directly give a performance report BeanCopy framework for performance comparison Conclusion map:

image.png


Select the frame

My main push two categories

  • Based MapStruct * Selma annotation type Mapper MapStruct and Selma are based annotation processor implemented on the annotation processor to write a blog introduced me alone, time to add a link here. MapStruct JSR 269 is a Java-based annotation processor, in the course need to be configured to run only after the completion of mvn compile will find the target folder to generate a class that implements a mapper interface. Open implementation class will find entity classes automatically generated fields one to one get, set methods of file. For example, I define a MapStruct Interface (@Mapper annotation support IOC injection method, I did not use here)
@Mapper
public interface PersonMapper {
    PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);

    /**
     * source -> destination
     *
     * @param car
     * @return
     */
    @Mappings({
        @Mapping(source = "middleName", target = "middle"),
        @Mapping(target = "email", ignore = true)
    })
    PersonDestination sourceToDestination(PersonSource car);
}

复制代码

After compiling you will find more than a target implementation class

image.png

By the same token we look at Selma

@Mapper
public interface SelmaPersonMapper {
    SelmaPersonMapper INSTANCE = Selma.mapper(SelmaPersonMapper.class);

    /**
     * source -> destination
     *
     * @param car
     * @return
     * @Maps(withCustomFields = {
     * @Field({"middleName", "middle"})
     * }, withIgnoreFields = {"email"})
     */
    @Maps(withCustomFields = {
        @Field({"middleName", "middle"})
    }, withIgnoreFields = {"email"})
    PersonDestination sourceToDestination(PersonSource car);
}

复制代码

So Selma and MapStruct are very similar, the same principle, and almost the same as in the notes and usage, I think MapStruct better mainly due to more active community, with SpringBoot better integrated, and the generated code more standardized, simple, beautiful .

  • Package based Orika, JMapper static tools (Dozer poor performance discarded) at the following code.
  private static final MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
        MapperFacade mapper = mapperFactory.getMapperFacade();
        PersonDestination orikaDestination = mapper.map(source, PersonDestination.class);
复制代码

If it is interchangeable List

 private static final MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
        MapperFacade mapper = mapperFactory.getMapperFacade();
        List<PersonSource> sourceList = Lists.newArrayList(source);
        List<PersonDestination> personDestinations = mapper.mapAsList(sourceList, PersonDestination.class);
复制代码

If there is a field name mapping

 private static final MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
        mapperFactory.classMap(PersonSource.class, PersonDestination.class)
            .field("firstName", "givenName")
            .field("lastName", "sirName")
            .byDefault()
            .register();
        MapperFacade mapper = mapperFactory.getMapperFacade();
        PersonDestination destination = mapper.map(source, PersonDestination.class);

复制代码

experiment

@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
@ToString
public class PersonSourceComputer {
    private String name;
    private BigDecimal price;
}

复制代码
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
@ToString
public class PersonSourceSon {
    private String sonName;
    private List<PersonSourceComputer> computers;
}

复制代码
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
@ToString
public class PersonSource {
    private String firstName;
    private String middleName;
    private String lastName;
    private String email;
    List<PersonSourceSon> son;

}
复制代码
public class BeanCopyTest {
    private static final Logger logger = LoggerFactory.getLogger(BeanCopyTest.class);
    private static final MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();

    //static {
    //    mapperFactory.classMap(PersonSource.class, PersonDestination.class).byDefault().register();
    //}

    public static void main(String[] args) {

        for (int i = 1; i < 11; i++) {
            beanCopyTest(i);
        }

    }

    private static void beanCopyTest(int i) {
        PersonSource source = initAndGetPersonSource();

        Stopwatch stopwatch = Stopwatch.createStarted();
        // MapStruct
        PersonDestination destination = PersonMapper.INSTANCE.sourceToDestination(source);
        System.out.println(destination);
        stopwatch.stop();
        logger.info("第" + i + "次" + "MapStruct cost:" + stopwatch.toString());

        // Selma
        stopwatch = Stopwatch.createStarted();
        PersonDestination selmaDestination = SelmaPersonMapper.INSTANCE.sourceToDestination(source);
        System.out.println(selmaDestination);
        stopwatch.stop();
        logger.info("第" + i + "次" + "Selma cost:" + stopwatch.toString());

        // BeanUtils
        stopwatch = Stopwatch.createStarted();
        PersonDestination bUtilsDestination = new PersonDestination();
        BeanUtils.copyProperties(source, bUtilsDestination);
        System.out.println(bUtilsDestination);
        stopwatch.stop();
        logger.info("第" + i + "次" + "BeanUtils cost:" + stopwatch.toString());

        // BeanCopier
        stopwatch = Stopwatch.createStarted();
        BeanCopier beanCopier = BeanCopier.create(PersonSource.class, PersonDestination.class, false);
        PersonDestination bcDestination = new PersonDestination();
        beanCopier.copy(source, bcDestination, null);
        System.out.println(bcDestination);
        stopwatch.stop();
        logger.info("第" + i + "次" + "BeanCopier cost:" + stopwatch.toString());

        // Orika
        stopwatch = Stopwatch.createStarted();
        MapperFacade mapper = mapperFactory.getMapperFacade();
        PersonDestination orikaDestination = mapper.map(source, PersonDestination.class);
        System.out.println(orikaDestination);
        stopwatch.stop();
        logger.info("第" + i + "次" + "Orika cost:" + stopwatch.toString());
    }

    private static PersonSource initAndGetPersonSource() {
        PersonSource source = new PersonSource();
        // set some field values
        source.setFirstName("firstName");
        source.setMiddleName("middleName");
        source.setLastName("lastName");
        source.setEmail("email");
        source.setSon(Lists.newArrayList(new PersonSourceSon(
            "sonName", Lists.newArrayList(new PersonSourceComputer("macBook", BigDecimal.valueOf(15000)))
        )));
        return source;
    }
}

复制代码
17:56:30.029 [main] INFO com.alibaba.beancp.BeanCopyTest - 第1次MapStruct cost:4.179 ms
17:56:30.035 [main] INFO com.alibaba.beancp.BeanCopyTest - 第1次Selma cost:2.727 ms
17:56:30.095 [main] INFO com.alibaba.beancp.BeanCopyTest - 第1次BeanUtils cost:59.65 ms
17:56:30.139 [main] INFO com.alibaba.beancp.BeanCopyTest - 第1次BeanCopier cost:43.52 ms
17:56:30.306 [main] INFO com.alibaba.beancp.BeanCopyTest - 第1次Orika cost:167.1 ms
17:56:30.306 [main] INFO com.alibaba.beancp.BeanCopyTest - 第2次MapStruct cost:88.97 μs
17:56:30.306 [main] INFO com.alibaba.beancp.BeanCopyTest - 第2次Selma cost:36.72 μs
17:56:30.306 [main] INFO com.alibaba.beancp.BeanCopyTest - 第2次BeanUtils cost:68.76 μs
17:56:30.307 [main] INFO com.alibaba.beancp.BeanCopyTest - 第2次BeanCopier cost:62.75 μs
17:56:30.307 [main] INFO com.alibaba.beancp.BeanCopyTest - 第2次Orika cost:108.0 μs
17:56:30.307 [main] INFO com.alibaba.beancp.BeanCopyTest - 第3次MapStruct cost:63.29 μs
17:56:30.307 [main] INFO com.alibaba.beancp.BeanCopyTest - 第3次Selma cost:71.12 μs
17:56:30.307 [main] INFO com.alibaba.beancp.BeanCopyTest - 第3次BeanUtils cost:81.64 μs
17:56:30.308 [main] INFO com.alibaba.beancp.BeanCopyTest - 第3次BeanCopier cost:68.01 μs
17:56:30.308 [main] INFO com.alibaba.beancp.BeanCopyTest - 第3次Orika cost:112.4 μs
17:56:30.308 [main] INFO com.alibaba.beancp.BeanCopyTest - 第4次MapStruct cost:54.27 μs
17:56:30.308 [main] INFO com.alibaba.beancp.BeanCopyTest - 第4次Selma cost:37.97 μs
17:56:30.308 [main] INFO com.alibaba.beancp.BeanCopyTest - 第4次BeanUtils cost:124.3 μs
17:56:30.308 [main] INFO com.alibaba.beancp.BeanCopyTest - 第4次BeanCopier cost:124.9 μs
17:56:30.309 [main] INFO com.alibaba.beancp.BeanCopyTest - 第4次Orika cost:107.3 μs
17:56:30.309 [main] INFO com.alibaba.beancp.BeanCopyTest - 第5次MapStruct cost:43.39 μs
17:56:30.309 [main] INFO com.alibaba.beancp.BeanCopyTest - 第5次Selma cost:50.03 μs
17:56:30.309 [main] INFO com.alibaba.beancp.BeanCopyTest - 第5次BeanUtils cost:75.00 μs
17:56:30.309 [main] INFO com.alibaba.beancp.BeanCopyTest - 第5次BeanCopier cost:50.83 μs
17:56:30.310 [main] INFO com.alibaba.beancp.BeanCopyTest - 第5次Orika cost:92.18 μs
17:56:30.310 [main] INFO com.alibaba.beancp.BeanCopyTest - 第6次MapStruct cost:34.60 μs
17:56:30.310 [main] INFO com.alibaba.beancp.BeanCopyTest - 第6次Selma cost:61.26 μs
17:56:30.310 [main] INFO com.alibaba.beancp.BeanCopyTest - 第6次BeanUtils cost:118.6 μs
17:56:30.310 [main] INFO com.alibaba.beancp.BeanCopyTest - 第6次BeanCopier cost:102.7 μs
17:56:30.311 [main] INFO com.alibaba.beancp.BeanCopyTest - 第6次Orika cost:170.5 μs
17:56:30.311 [main] INFO com.alibaba.beancp.BeanCopyTest - 第7次MapStruct cost:65.29 μs
17:56:30.311 [main] INFO com.alibaba.beancp.BeanCopyTest - 第7次Selma cost:52.06 μs
17:56:30.311 [main] INFO com.alibaba.beancp.BeanCopyTest - 第7次BeanUtils cost:86.51 μs
17:56:30.311 [main] INFO com.alibaba.beancp.BeanCopyTest - 第7次BeanCopier cost:101.3 μs
17:56:30.312 [main] INFO com.alibaba.beancp.BeanCopyTest - 第7次Orika cost:119.0 μs
17:56:30.312 [main] INFO com.alibaba.beancp.BeanCopyTest - 第8次MapStruct cost:56.88 μs
17:56:30.312 [main] INFO com.alibaba.beancp.BeanCopyTest - 第8次Selma cost:35.56 μs
17:56:30.312 [main] INFO com.alibaba.beancp.BeanCopyTest - 第8次BeanUtils cost:98.93 μs
17:56:30.312 [main] INFO com.alibaba.beancp.BeanCopyTest - 第8次BeanCopier cost:69.25 μs
17:56:30.312 [main] INFO com.alibaba.beancp.BeanCopyTest - 第8次Orika cost:95.27 μs
17:56:30.313 [main] INFO com.alibaba.beancp.BeanCopyTest - 第9次MapStruct cost:33.60 μs
17:56:30.313 [main] INFO com.alibaba.beancp.BeanCopyTest - 第9次Selma cost:31.90 μs
17:56:30.313 [main] INFO com.alibaba.beancp.BeanCopyTest - 第9次BeanUtils cost:96.19 μs
17:56:30.313 [main] INFO com.alibaba.beancp.BeanCopyTest - 第9次BeanCopier cost:77.15 μs
17:56:30.313 [main] INFO com.alibaba.beancp.BeanCopyTest - 第9次Orika cost:95.17 μs
17:56:30.313 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次MapStruct cost:45.55 μs
17:56:30.314 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次Selma cost:32.28 μs
17:56:30.314 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次BeanUtils cost:62.06 μs
17:56:30.314 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次BeanCopier cost:46.78 μs
17:56:30.314 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次Orika cost:113.6 μs

复制代码
17:56:30.306 [main] INFO com.alibaba.beancp.BeanCopyTest - 第1次Orika cost:167.1 ms
17:56:30.307 [main] INFO com.alibaba.beancp.BeanCopyTest - 第2次Orika cost:108.0 μs
17:56:30.308 [main] INFO com.alibaba.beancp.BeanCopyTest - 第3次Orika cost:112.4 μs
17:56:30.309 [main] INFO com.alibaba.beancp.BeanCopyTest - 第4次Orika cost:107.3 μs
17:56:30.310 [main] INFO com.alibaba.beancp.BeanCopyTest - 第5次Orika cost:92.18 μs
17:56:30.311 [main] INFO com.alibaba.beancp.BeanCopyTest - 第6次Orika cost:170.5 μs
17:56:30.312 [main] INFO com.alibaba.beancp.BeanCopyTest - 第7次Orika cost:119.0 μs
17:56:30.312 [main] INFO com.alibaba.beancp.BeanCopyTest - 第8次Orika cost:95.27 μs
17:56:30.313 [main] INFO com.alibaba.beancp.BeanCopyTest - 第9次Orika cost:95.17 μs
17:56:30.314 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次Orika cost:113.6 μs

复制代码

17:56:30.029 [main] INFO com.alibaba.beancp.BeanCopyTest - 第1次MapStruct cost:4.179 ms
17:56:30.306 [main] INFO com.alibaba.beancp.BeanCopyTest - 第2次MapStruct cost:88.97 μs
17:56:30.307 [main] INFO com.alibaba.beancp.BeanCopyTest - 第3次MapStruct cost:63.29 μs
17:56:30.308 [main] INFO com.alibaba.beancp.BeanCopyTest - 第4次MapStruct cost:54.27 μs
17:56:30.309 [main] INFO com.alibaba.beancp.BeanCopyTest - 第5次MapStruct cost:43.39 μs
17:56:30.310 [main] INFO com.alibaba.beancp.BeanCopyTest - 第6次MapStruct cost:34.60 μs
17:56:30.311 [main] INFO com.alibaba.beancp.BeanCopyTest - 第7次MapStruct cost:65.29 μs
17:56:30.312 [main] INFO com.alibaba.beancp.BeanCopyTest - 第8次MapStruct cost:56.88 μs
17:56:30.313 [main] INFO com.alibaba.beancp.BeanCopyTest - 第9次MapStruct cost:33.60 μs
17:56:30.313 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次MapStruct cost:45.55 μs

复制代码
17:56:30.313 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次MapStruct cost:45.55 μs
PersonDestination(firstName=firstName, middle=middleName, lastName=lastName, email=null, son=[PersonDestinationSon(sonName=sonName, computers=[PersonDestinationComputer(name=macBook, price=15000)])])
17:56:30.314 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次Selma cost:32.28 μs
PersonDestination(firstName=firstName, middle=null, lastName=lastName, email=email, son=[PersonSourceSon(sonName=sonName, computers=[PersonSourceComputer(name=macBook, price=15000)])])
17:56:30.314 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次BeanUtils cost:62.06 μs
PersonDestination(firstName=firstName, middle=null, lastName=lastName, email=email, son=[PersonSourceSon(sonName=sonName, computers=[PersonSourceComputer(name=macBook, price=15000)])])
17:56:30.314 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次BeanCopier cost:46.78 μs
PersonDestination(firstName=firstName, middle=null, lastName=lastName, email=email, son=[PersonDestinationSon(sonName=sonName, computers=[PersonDestinationComputer(name=macBook, price=15000)])])
17:56:30.314 [main] INFO com.alibaba.beancp.BeanCopyTest - 第10次Orika cost:113.6 μs

复制代码

Experimental results:

1. Regardless of what kind of framework experiment, in fact, the absolute value of the performance difference is not too large (non-first run). 2. Individual reference frame is copied PersonSourceSon, the individual is PersonDestinationSon, illustrate the different shades on different frame to achieve copy protection scheme. 3. When there is a field name mapping, ignore, formatting and other needs, different frame supports.

to sum up

1. In the daily development, BeanCopy needs of three types of

  • The simplest copy the same field
  • Complex of copy (such as field names are different, there ignore demand, formatting requirements)
  • There are business logic copy

So for more than three points, I think

  • The first in a simple and efficient based, I suggest that the direct use Orika tools, implementation is very simple client encoding is very small, basically lose a source and target type to go to ensure that the deep copy, higher than the performance Dozer and other old products, and between a set of copies is also very good. Like BeanUtils, BeanCopier in many scenarios the performance was not as good Orika, there will be all kinds of problems much Tucao.
  • The second is recommended to use a powerful MapStruct framework, its benefits, that is not only generated the code, more intuitive and easy to debug. And support for very large and powerful annotations, you can easily do field mapping, field ignore, date formatting, formatting between the amount of multi-level and so on. There mapping template inheritance to reuse and combination of functions. There is a natural support Spring injection, SpringBoot integration, at this point, compared Dozer xml-style mapping annotations are more in line with modern programming mode.

2. offs on performance

We can find the performance test, once run once, the absolute value of several single-copy performance framework above are very low (individual framework is primarily based on time-consuming Asm beginning of the buffer principle, jvm hot code optimization and other reasons will be the first time a little longer). So consider the trade-offs in performance, mainly based on the amount and systems scenes. If it is particularly exaggerated concurrent, or really need to optimize the library system to enhance the performance bottlenecks. This low relative gap between the absolute sense only, because the difference between single microseconds, if not a product of the amount of amplification, the difference in performance can be ignored. Most companies do not have this normal demand, there is no need to pursue this extreme performance, so more is considered both in a "high performance" performance (absolute value), so that you are satisfied with other aspects of the library.

Guess you like

Origin juejin.im/post/5dd672e2e51d4536d737d504