In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/01 Report--
This article mainly talks about "what are the Java object replication tool classes", interested friends may wish to have a look. The method introduced in this paper is simple, fast and practical. Next let the editor to take you to learn "what are the Java object replication tool classes?"
In daily programming, we often encounter scenarios where object attributes are copied, such as the following common three-tier MVC architecture.
When we program under the above architecture, we usually need to go through object transformation, for example, after the business request process goes through a three-tier structure, we need to convert DTO to DO and save it in the database.
When you need to display the query data from the data page, the query data will go through a three-tier architecture from DO to DTO, and finally to VO, and then displayed on the page.
When the business is simple, we write the code by hand and copy the object properties through getter/setter, which is very simple. But once the business becomes complex and there are a lot of object properties, handwritten code can become a programmer's nightmare.
Handwriting is not only tedious and time-consuming, but also error-prone.
Ah Fan has experienced a project before, and there are about 40 or 50 field properties in an object. At that time, A Fan was just getting started and didn't know much about anything. He wrote getter/setter to copy object properties for a long time.
Voiceover: it is obviously unreasonable for an object to have so many attributes, and we should split it in the design process.
Until later, I learned about the object attribute copy tool class, and after using it, I found that it was really fragrant, and I no longer had to write code by hand. Later, encountered more and more tool classes, although the core functions are the same, but there are still a lot of differences. When a novice sees this, he may look confused and don't know how to choose.
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, which is the attribute assignment of different types of basic functions, such as different field name attribute assignments such as basic type and its packaging type, of course, the field name should be consistent as far as possible, but in the actual business, due to different developers, or misspelled words, these reasons may lead to a shallow / deep copy of inconsistent field names, and a shallow copy will refer to the same object, if slightly careless If you 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
Suppose we have the following categories in our project:
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 ("Ah Fan")
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 figure above, we can draw some conclusions from BeanUtils:
Properties with inconsistent ordinary field names cannot be copied from nested object fields, and the same object will be used as the source object. Even if fields with inconsistent shallow copy types are used, the default type conversion will be performed.
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.
Image-20200818222213879Spring 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, which is the same as above
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. Ah Fan didn't pay attention to it before and used the Spring utility class, but it was used in accordance with the usage of Apache.
Compare the studentDO with the studentDTO object at this point:
From the comparison above, we can draw some conclusions:
The field name is inconsistent, 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, which can convert nested object fields, it 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 Ah Fan in his daily work, while the following ones are only recently contacted by Ah Fan, 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, 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.
/ / Note that the last property is set to true
BeanCopier 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.
The official website logo
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 A Fan came across this tool class, he was deeply impressed. It was so powerful that all the functions we expected above have been realized by Dozer.
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:
/ / the code that omits the attribute
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:
XMLAPI comments
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 the 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 the picture above, we can see:
For fields with inconsistent types, attributes are replicated. DO and DTO object fields are not the same object, that is, deep copies are mapped by configuring field names, and 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:
@ Autowired
Mapper mapper
Public void objMapping (StudentDTO studentDTO) {
/ / use it directly
StudentDO studentDO =
Mapper.map (studentDTO, StudentDO.class)
}
Annotation mode
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.
@ Data
Public 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:
/ / other setting codes are omitted
/ / Don't set the time here yet.
/ / studentDTO.setCreateDate ("2020-08-08")
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 the DTO and DO objects:
Some features of orika can be found in the above figure:
The default supports type inconsistency (basic type / wrapper type) conversion supports deep copy to specify different field name mapping relationships, and attributes can be copied successfully.
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:
@ Mapper
Public 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 code
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
The output DO and DTO are as follows:
From the above picture, we can draw some conclusions:
Some types are inconsistent and can be converted automatically, such as basic types and String deep copies of basic types and packaging types
The above examples 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: https://github.com/mapstruct/mapstruct-examples.
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.
At this point, I believe you have a deeper understanding of the "Java object replication tool class", might as well come to the actual operation of it! Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!
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.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.