Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What are the object replication utility classes of Java and how to use them

2025-02-24 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/01 Report--

Today, I would like to share with you what the Java object replication tool class has and how to use the relevant knowledge points, detailed content, clear logic, I believe that most people still know too much about this knowledge, so share this article for your reference, I hope you can get something after reading this article, let's take a look at it.

Tool class properties

Before we introduce these utility classes, let's take a look at what features are required for the next useful attribute replication utility class:

Basic attribute replication, this is the basic function

Different types of attribute assignments, such as basic types and their packaging types, etc.

Different field name attribute assignments, of course, field names should be consistent as far as possible, but in actual business, due to different developers, or misspelling words, these reasons may lead to inconsistent field names.

Shallow copy / deep copy, shallow copy will refer to the same object, if slightly careless, change the object at the same time, you will step on an unexpected pit

Let's start with the utility class.

Apache BeanUtils

The first introduction is the most famous tool class "Apache BeanUtils" that should be copied from Java domain attributes, which many people must have used or seen more or less.

It doesn't matter if you haven't used it, let's show you the use of this class, which is very simple.

First of all, let's introduce dependency, which uses the latest version:

Commons-beanutils commons-beanutils 1.9.4

At this point, we need to convert the DTO object to the DO object, and we only need to simply call the BeanUtils#copyProperties method to copy the object properties.

StudentDTO studentDTO = new StudentDTO (); studentDTO.setName (editor); studentDTO.setAge (18); studentDTO.setNo ("6666"); List subjects = new ArrayList (); subjects.add ("math"); subjects.add ("english"); studentDTO.setSubjects (subjects); studentDTO.setCourse (new Course ("CS-1")); studentDTO.setCreateDate ("2020-08-08"); StudentDO studentDO = new StudentDO (); BeanUtils.copyProperties (studentDO, studentDTO)

However, if you write the above code like this, we will encounter the first problem. BeanUtils does not support the conversion of String to Date by default.

To solve this problem, we need to construct a Converter transformation class ourselves, and then register using ConvertUtils, using the following methods:

ConvertUtils.register (new Converter () {@ SneakyThrows @ Override public Date convert (Class type, Object value) {if (value = = null) {return null;} if (value instanceof String) {String str = (String) value; return (Date) DateUtils.parseDate (str, "yyyy-MM-dd");} return null;}}, Date.class)

At this point, we observe the studentDO and studentDTO object property values:

From the above, we can draw some conclusions from BeanUtils:

Properties with inconsistent ordinary field names cannot be copied

Nested object fields that will use the same object as the source object, even with a shallow copy

Fields with inconsistent types will be converted by default.

Although BeanUtils is very convenient to use, but its underlying source code in order to pursue perfection, add too much packaging, use a lot of reflection, do a lot of verification, resulting in poor performance, so Alibaba development manual mandatory provisions to avoid using Apache BeanUtils.

Spring BeanUtils

The Spring attribute copy tool class name is the same as Apache, and the basic usage is similar. Let me first take a look at the basic usage of Spring BeanUtils.

Again, let's introduce dependencies first. As we can see from the name, BeanUtils is located in the Spring-Beans module, where we still use the latest module.

Org.springframework spring-beans 5.2.8.RELEASE

Here we reuse the above example with DTO and DO, and the conversion code is as follows:

/ / omit the above assignment code and agree with StudentDO studentDO = new StudentDO (); BeanUtils.copyProperties (studentDTO, studentDO)

From the usage, we can see that there is a biggest difference between Spring BeanUtils and Apache. The positions of the parameters of the source object and the target object are different. The editor did not notice that the Spring utility class was used before, but it was used in accordance with the usage of Apache.

From the above comparison, we can draw some conclusions:

The field name is inconsistent and the property cannot be copied

The type is inconsistent and the property cannot be copied. Note, however, that if the type is a basic type and a wrapper class of the basic type, this can be converted to

Nested object fields that will use the same object as the source object, even with a shallow copy

In addition to this method, Spring BeanUtils provides an overloaded method:

Public static void copyProperties (Object source, Object target, String... IgnoreProperties)

Using this method, we can ignore some properties that we do not want to be copied in the past:

BeanUtils.copyProperties (studentDTO, studentDO, "name")

In this way, the name property is not copied into the DO object.

Although the function of Spring BeanUtils is similar to that of Apache BeanUtils, Spring BeanUtils still beats Apache BeanUtils in terms of performance. The main reason is that Spring does not do too much parity with reflection like Apache, and Spring BeanUtils uses caching internally to speed up the conversion.

So to choose between the two, it is recommended to use Spring BeanUtils.

Cglib BeanCopier

The above two are often used by editors in their daily work, while the following ones are only recently contacted by editors, such as Cglib BeanCopier. This usage method may be a little more complicated than the above two classes, so let's take a look at the specific usage:

First, we introduce Cglib dependencies:

Cglib cglib 3.3.0

Voiceover: if you still have Spring-Core in your project, if you look up the class BeanCopier, you can find two different packages with the same name.

One belongs to Cglib and the other belongs to Spring-Core.

In fact, BeanCopier in Spring-Core actually introduces classes in Cglib. The purpose of this is to ensure the stability of Spring usage length Cglib related classes, and to prevent external Cglib dependencies from being inconsistent, resulting in abnormal operation of Spring.

The conversion code is as follows:

/ / omit the assignment statement StudentDO studentDO = new StudentDO (); BeanCopier beanCopier = BeanCopier.create (StudentDTO.class, StudentDO.class, false); beanCopier.copy (studentDTO, studentDO, null)

Using BeanCopier is a little more than BeanUtils. Compare studentDO with studentDTO objects:

From the above, we can draw a conclusion that is basically consistent with Spring Beanutils:

The field name is inconsistent and the property cannot be copied

The type is inconsistent and the property cannot be copied. However, it is a little different, if the type is a basic type / basic type wrapper type, the two cannot be copied.

Nested object fields that will use the same object as the source object, even with a shallow copy

Above we use Beanutils, encounter this kind of field name, type is not consistent this kind of situation, we have no good way, can only handwritten hard coding.

However, under BeanCopier, we can introduce converters for type conversion.

/ notice that the last property is set to trueBeanCopier beanCopier = BeanCopier.create (StudentDTO.class, StudentDO.class, true); / / Custom Converter beanCopier.copy (studentDTO, studentDO, new Converter () {@ Override public Object convert (Object source, Class target, Object context) {if (source instanceof Integer) {Integer num = (Integer) source; return num.toString ();} return null;}})

But complain about the converter, once we turn it on and use the converter, we have to do all the attribute replication ourselves. For example, in the above example, we only deal with the case where the source object field type is Integer, but nothing else. We get that the DO object will only be copied with the name attribute.

The principle of Cglib BeanCopier is different from the above two Beanutils principles. It mainly uses bytecode technology to dynamically generate a proxy class, which implements get and set methods. There is some overhead in the process of generating proxy classes, but once it is generated, we can cache it and reuse it, and the performance of all Cglib is better than that of the above two Beanutils.

Dozer

Dozer, literally translated as excavator, this is a "heavyweight" attribute copy tool class, compared to the above three tool classes, Dozer has many powerful functions.

Voiceover: heavyweight / lightweight is just a relative term, and Dozer is a relatively heavyweight tool class because it has a lot of advanced features compared to tools like BeanUtils.

As soon as the editor came across this tool class, he was deeply impressed, it was really too powerful, the above functions we expected, Dozer have been realized for you.

Let's take a look at how to use it. First, we introduce Dozer dependencies:

Net.sf.dozer dozer 5.4.0

The method of use is as follows:

/ / Code for omitting attributes DozerBeanMapper mapper = new DozerBeanMapper (); StudentDO studentDO = mapper.map (studentDTO, StudentDO.class); System.out.println (studentDO)

Dozer requires us to create a new DozerBeanMapper, which is equivalent to BeanUtils, responsible for mapping between objects and attribute replication.

Voiceover: as we can see in the following code, the configuration file needs to be loaded to generate a DozerBeanMapper instance, which is expensive to generate at will. In our application, we should use singleton mode and reuse DozerBeanMapper.

If the attributes are simple basic types, then we can quickly copy the attributes by using the above code.

Unfortunately, we have string and Date type conversions in our code, and if we use the above code directly, the program will throw an exception.

So here we need to use the powerful configuration features of Dozer. We can use the following three ways:

XML

API

Notes

Among them, the way of API is more tedious, most of which are carried out by XML at present, and the other annotation function is the new function added after Dozer 5.3.2, but the function is weaker than XML.

How XML is used

Next, we use XML configuration method to configure the relationship between DTO and DO. First, we create a new dozer/dozer-mapping.xml file:

Com.just.doone.example.domain.StudentDTO com.just.doone.example.domain.StudentDO no number createDate createDate

Then modify our Java code to add the configuration file to read Dozer:

DozerBeanMapper mapper = new DozerBeanMapper (); List mappingFiles = new ArrayList (); / / read configuration file mappingFiles.add ("dozer/dozer-mapping.xml"); mapper.setMappingFiles (mappingFiles); StudentDO studentDO = mapper.map (studentDTO, StudentDO.class); System.out.println (studentDO)

After running, compare the studentDO with the studentDTO object:

From above, we can find:

Fields with inconsistent types, properties are copied

DO and DTO object fields are not the same object, that is, deep copy

By configuring the mapping of field names, the properties of different fields are also copied.

In addition to the relatively simple attributes mentioned above, Dozer supports many additional features, such as enumerated attribute replication, Map and other collection attribute replication.

Some friends have just seen the use of Dozer, may think that this tool class is more tedious, unlike the BeanUtils tool class can be solved with a single line of code.

In fact, Dozer can be well integrated with the Spring framework. We can configure it in advance in the Spring configuration file. Later, we just need to refer to the corresponding Bean of Dozer, using a single line of code.

Dozer is integrated with Spring. We can use its DozerBeanMapperFactoryBean, which is configured as follows:

DozerBeanMapperFactoryBean supports setting many properties, you can customize the type conversion, and you can set other properties.

There is also an easy way to configure DozerBeanMapper in XML:

Dozer/dozer-Mapperpping.xml

After the Spring configuration is complete, we can directly inject into the code:

@ AutowiredMapper mapper;public void objMapping (StudentDTO studentDTO) {/ / directly use StudentDO studentDO = mapper.map (studentDTO, StudentDO.class);} annotation method

Compared with the XML configuration, the Dozer annotation method is very weak and can only complete the mapping of inconsistent field names.

In the above code, we can use the @ Mapping annotation on the no field of DTO so that when we complete the conversion using Dozer, the field property will be copied.

@ Datapublic class StudentDTO {private String name; private Integer age; @ Mapping ("number") private String no; private List subjects; private Course course; private String createDate;}

Although the annotation function is a little weak at present, a new annotation function may be added in the later version, and XML can be used with annotations.

Finally, the bottom layer of Dozer essentially uses reflection to copy attributes, so the execution speed is not so ideal.

Orika

Orika is also a heavyweight attribute replication tool class similar to Dozer, which also provides similar functions such as Dozer. But orika does not need to use tedious XML configuration, it itself provides a very simple set of API usage, very easy to use.

First, let's introduce its latest dependency:

Ma.glasnost.orika orika-core 1.5.4

The basic usage is as follows:

/ / omit other setting codes / / do not set studentDTO.setCreateDate ("2020-08-08") here; MapperFactory mapperFactory = new DefaultMapperFactory.Builder (). Build (); MapperFacade mapper = mapperFactory.getMapperFacade (); StudentDO studentDO = mapper.map (studentDTO, StudentDO.class)

Here we introduce two classes, MapperFactory and MapperFacade, in which MapperFactory can be used for field mapping, configuration converter, and so on, and MapperFacade, like Beanutils, is responsible for mapping between objects.

In the above code, we deliberately annotate the setting value of the createDate time property in the DTO object, because by default, if there is no separate converter for the time type, the above code will be thrown wrong.

In addition, in the above code, properties with inconsistent field names will not be copied, so we need to set them separately.

Let's set up a time converter and specify the field name:

MapperFactory mapperFactory = new DefaultMapperFactory.Builder (). Build (); ConverterFactory converterFactory = mapperFactory.getConverterFactory (); converterFactory.registerConverter (new DateToStringConverter ("yyyy-MM-dd")); mapperFactory.classMap (StudentDTO.class, StudentDO.class) .field ("no", "number") / / be sure to call byDefault .byDefault () .register (); MapperFacade mapper = mapperFactory.getMapperFacade (); StudentDO studentDO = mapper.map (studentDTO, StudentDO.class)

In the above code, first we need to register a time-type converter with ConverterFactory, and then we need to MapperFactory to specify the mapping between different field names.

Here we should note that after we use classMap, if we want the same field name attribute to be copied by default, then be sure to call the byDefault method.

Briefly compare DTO and DO objects

You can find some features of orika:

The default supports type inconsistency (basic type / wrapper type) conversion

Support for deep copy

Specify a different field name mapping relationship, and the attribute can be successfully copied.

In addition, orika also supports collection mapping:

MapperFactory mapperFactory = new DefaultMapperFactory.Builder () .build (); List persons = new ArrayList (); List personDtos = mapperFactory.getMapperFacade () .mapAsList (persons, PersonDto.class)

Finally, let's talk about the implementation principle of orika. Orika is different from the underlying principle of dozer, which uses the bytecode of the mapping of field attributes generated by javassist, and then dynamically loads the execution bytecode file, which is much faster than the tool class that reflects the original Dozer.

MapStruct

Unwittingly, five attribute copy tool classes have been written in one breath, and all the friends see this, so don't give up, stick to it, and the following will introduce a tool class "MapStruct" that is not the same as those above.

The utility classes described above, whether using reflection or bytecode techniques, need to be executed dynamically while the code is running, so they are much slower than handwritten hard-coding.

Is there a tool class that runs almost as fast as hard-coding?

This will introduce the utility class MapStruct, which runs almost as fast as hard coding, because it generates Java Bean attribute copy code during compilation, and does not need to use reflection or bytecode technology during run time, so high performance is ensured.

In addition, because the code is generated during compilation, if there are any problems, they can be exposed in advance during compilation, which allows developers to solve the problem ahead of time without having to wait until the code application is online and find the error after running.

Let's take a look at how to use this utility class. First, let's introduce this dependency:

Org.mapstruct mapstruct 1.3.1.Final

Second, because MapStruct needs to generate code during the compiler, we need to configure it in the maven-compiler-plugin plug-in:

Org.apache.maven.plugins maven-compiler-plugin 3.8.1 1.8 1.8 org.mapstruct mapstruct-processor 1.3.1.Final

Next, we need to define the mapping interface, as follows:

Mapperpublic interface StudentMapper {StudentMapper INSTANCE = Mappers.getMapper (StudentMapper.class); @ Mapping (source = "no", target = "number") @ Mapping (source = "createDate", target = "createDate", dateFormat = "yyyy-MM-dd") StudentDO dtoToDo (StudentDTO studentDTO);}

We need to define a transformation interface using the MapStruct annotation @ Mapper, so that StudentMapper functions the same as utility classes such as BeanUtils.

Second, because of the inconsistency of field names in our DTO and DO objects, we also use the @ Mapping annotation on the transformation method to specify the field mapping. In addition, our createDate field types are not consistent, here we also need to specify the time to format the type.

Once the above definition is complete, we can directly use a single line of StudentMapper code to complete the object conversion.

/ / ignore other codes StudentDO studentDO = StudentMapper.INSTANCE.dtoToDo (studentDTO)

If our object uses Lombok, specify a different field name with @ Mapping, and the following error may be thrown during compilation:

The main reason for this is that Lombok also needs to automatically generate code during compilation, which may lead to a conflict between the two. When MapStruct generates code, there is no code generated by Lombok.

The solution can be to add Lombok to the maven-compiler-plugin plug-in configuration as follows:

Org.apache.maven.plugins maven-compiler-plugin 3.8.1 1.8 1.8 org.mapstruct mapstruct-processor 1.3.1.Final org.projectlombok lombok 1.18.12

Some types are inconsistent and can be converted automatically, such as

Basic types and Packaging types

Basic types of packaging types and String

Deep copy

The examples introduced above introduce some simple field mappings. If your partner encounters other scenarios at work, you can check the project first to see if there is a final solution.

Now that we know that MapStruct generates code during compilation, let's take a look at automatically generated code:

Public class StudentMapperImpl implements StudentMapper {public StudentMapperImpl () {} public StudentDO dtoToDo (StudentDTO studentDTO) {if (studentDTO = = null) {return null;} else {StudentDO studentDO = new StudentDO (); studentDO.setNumber (studentDTO.getNo ()) Try {if (studentDTO.getCreateDate ()! = null) {studentDO.setCreateDate ((new SimpleDateFormat ("yyyy-MM-dd")) .parse (studentDTO.getCreateDate ());}} catch (ParseException var4) {throw new RuntimeException (var4);} studentDO.setName (studentDTO.getName ()) If (studentDTO.getAge ()! = null) {studentDO.setAge (String.valueOf (studentDTO.getAge ();} List list = studentDTO.getSubjects (); if (list! = null) {studentDO.setSubjects (new ArrayList (list));} studentDO.setCourse (studentDTO.getCourse ()) Return studentDO;}

From the point of view of the generated code, there is no dark magic. MapStruct automatically generates an implementation class StudentMapperImpl, which implements dtoToDo, and the method calls getter/setter to set the value.

From this, we can see that MapStruct is equivalent to setting values for our handwritten getter/setter, so its performance will be very good.

These are all the contents of this article entitled "what are the object replication tools of Java and how to use them?" Thank you for reading! I believe you will gain a lot after reading this article. The editor will update different knowledge for you every day. If you want to learn more knowledge, please pay attention to the industry information channel.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report