This is the 7th day of my participation in the November Gwen Challenge. Check out the details: The last Gwen Challenge 2021

Object Copy Status

Business systems in the two objects are often required properties of copy, can’t deny the objects one by one copy is most quick and safe, but when the number of property field data object more than the programmer’s tolerance, code therefore become bloated, using some convenient tool to copy objects will be a good choice.

Model data transformation

Project will more or less of certain entities transform (DTO, VO, DO or PO, etc.), often have the same attribute names, a small number of cases we can directly take the set and get methods for the assignment, but if the transformation is used in many places, or to operate by the set will greatly affect the development efficiency.

  • For entity conversion, we have one entity for a table (this can be thought of as a DO).

  • Interaction with the third party in the business data, we need to get real data to them, but is not necessarily a DO all of the attributes that are likely to reduce or more DO the properties in the composition, here we introduce the dtos (we can remove some privacy information in this entity, such as: bank card number, id card, password).

  • For a gender, we use 1 and 2 to represent male and female. The page cannot display 1 or 2 directly, but it needs to display male, female or pretty boy (male) or pretty girl (female). At this time, it represents such an entity and we can regard it as VO.

The most popular tool classes are:

Two versions of Apache :(reflection mechanism)

  • org.apache.commons.beanutils.PropertyUtils.copyProperties(Object dest, Object orig)

Cause: dateTimeConveter’s conveter does not handle null values


// constraints on special targetObject attributes :(Date, BigDecimal, etc.)

public class BeanObject { // omit getter, setter methods here
    private String name;
    private java.util.Date date;
}
 public class BeanObjectTest {  
    public static void main(String args[]) throws Throwable  {  
    BeanObject from = new BeanObject(); 
    BeanObject to = new BeanObject();  
    //from.setDate(new java.util.Date());
    from.setName("TTTT");
    org.apache.commons.beanutils.BeanUtils.copyProperties(to, from);// If from.setDate is removed, a conveter exception occurs hereSystem.out.println(ToStringBuilder.reflectionToString(from)); System.out.println(ToStringBuilder.reflectionToString(to)); }}Copy the code
  • org.apache.commons.beanutils.BeanUtils.copyProperties(Object dest, Object orig)

  • The same attribute name, and the type does not match

  • Reason: these two utility classes do not support matching of the same name and different types!! Wrapper class Long and primitive data type Long are ok

public class SourceClass {  // omit getter, setter methods here
    private Long num;  
    private String name;
}

public class TargetClass {  // omit getter, setter methods here
    private Long num;
    private String name;
}

public class PropertyUtilsTest {
    public static void main(String args[]) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException  {
        SourceClass from = new SourceClass();
        from.setNum(1);
        from.setName("name");
        TargetClass to = new TargetClass();
        // Throw a parameter mismatch exception
        org.apache.commons.beanutils.PropertyUtils.copyProperties(to, from);
        org.springframework.beans.BeanUtils.copyProperties(from, to);
        // Throw a parameter mismatch exceptionSystem.out.println(ToStringBuilder.reflectionToString(from)); System.out.println(ToStringBuilder.reflectionToString(to)); }}Copy the code

Spring version :(reflection mechanism)

  • org.springframework.beans.BeanUtils.copyProperties(Object source, Object target, Class editable, String[] ignoreProperties)

Cglib version :(high efficiency with dynamic proxy)

Cglib is a relatively low-level framework for manipulating Java bytecode

  • net.sf.cglib.beans.BeanCopier.copy(Object paramObject1, Object paramObject2, Converter paramConverter)

Tool operation

Introduction of the principle

Reflection type :(apache)

Both use static class calls that ultimately convert two singleton utility objects in the virtual machine.

public BeanUtilsBean(a){
  this(new ConvertUtilsBean(), new PropertyUtilsBean());
}
Copy the code
  • Convertutilsbeans can be registered with ConvertUtils globally.

    • ConvertUtils.register(new DateConvert(), java.util.Date.class);

    • The copyProperties method of PropertyUtilsBean implements the copying algorithm.

  1. Instanceof DynaBean: Object value = ((DynaBean)orig).get(name); Then copy the value to the dynamic bean class.

  2. Map type: orig Instanceof Map: copies key values one by one

  3. Other common classes: Fetch name from beanInfo (each object has a cached bean information, including attribute fields, etc.) and copy sourceClass and targetClass one by one.

Cglib type: BeanCopier

copier = BeanCopier.create(source.getClass(), target.getClass(), false);
copier.copy(source, target, null);
Copy the code

Handling of mismatch between Get and set methods

public class BeanCopierTest {
    /** * We can see from this use case that every get method in the target.class of beancopier.create must have a queue set method *@param args
     */
    public static void main(String args[]) {  
        BeanCopier copier = BeanCopier.create(UnSatifisedBeanCopierObject.class, SourceClass.class,false);
        copier = BeanCopier.create(SourceClass.class, UnSatifisedBeanCopierObject.class, false); // An exception is thrown here}}class UnSatifisedBeanCopierObject {   
    private String name;
    private Long num;
    public String getName(a) {undefined
       return name;
    }
    public void setName(String name) {undefined
       this.name = name;
    }
    public Long getNum(a) {undefined
       return num;
    }
// public void setNum(Long num) {undefined
// this.num = num;
/ /}
}
Copy the code

Create the sourceClass-> TargetClass proxy class and put it into the JVM, so creating the proxy class is time-consuming. It is best to ensure the singleton pattern of this object, as described in the last section.

Create process -> see JDK for source code:

net.sf.cglib.beans.BeanCopier.Generator.generateClass(ClassVisitor)

  1. Get all public get methods on sourceClass – > PropertyDescriptor[] getters

  2. Get all public set methods of TargetClass – > PropertyDescriptor[] setters

  3. Iterate through each property of the setters, performing 4 and 5

  4. Generate all setters for sourceClass – getters for PropertyDescriptor

  5. PropertyDescriptor[] setters- setters of PropertyDescriptor

  6. Pair setter and getter names with types to generate copy methods of the proxy class.

The principle of summary

Copy attribute process: call generated proxy class, proxy class code and manual operation of the code is very similar, very high efficiency.

The fastest of these is BeanCopier, which copies only fields with the same name and type by default and does not copy if date is null.

I think that’s the best way to do it, for example, copy the value of object A into object B, and let’s copy the same, and let’s separate out the different fields that we need to personalize with GET, so that the program is very clear, and we can focus on different things.