Why not recommend using BeanUtils?

Posted by Oren on Fri, 18 Feb 2022 09:02:00 +0100

Author: Mingming Ruyue senior\
Source: blog csdn. net/w605283073/article/details/107371462

As mentioned in the column "attribute copying tool is not recommended", it is recommended to directly define the conversion classes and methods and use the IDEA plug-in to automatically fill in the get / set function.

The main reasons for not recommending are:

The performance of some attribute copying tools is a little poor, and some attribute copying tools have "bugs". Using attribute copying tools is prone to some hidden dangers (the following examples will be discussed)

2 example

First of all, the company encountered a real case of poor property copy performance of BeanUtils in commons package. Then the colleague replaced Spring's BeanUtils, which has much better performance. If you are interested, you can use the performance test framework or benchmark framework to compare. There is no comparison here.

Next, let's look at the problems with the property copy of Spring's BeanUtils:

import lombok.Data;
import java.util.List;

@Data
public class A {
    private String name;

    private List<Integer> ids;
}
@Data
public class B {
    private String name;

    private List<String> ids;
}
import org.springframework.beans.BeanUtils;

import java.util.Arrays;

public class BeanUtilDemo {
    public static void main(String[] args) {
        A first = new A();
        first.setName("demo");
        first.setIds(Arrays.asList(1, 2, 3));

        B second = new B();
        BeanUtils.copyProperties(first, second);
        for (String each : second.getIds()) {// Type conversion exception
            System.out.println(each);
        }
    }
}

When you run the above example, type conversion exceptions will occur.

At the break point, you can see that after the attribute is copied, ids in the second object of type B is still of Integer type:

If you do not convert it to a string, you can print it directly without reporting an error.

Using CGlib, you will encounter similar problems without defining Converter:

import org.easymock.cglib.beans.BeanCopier;

import java.util.Arrays;

public class BeanUtilDemo {
    public static void main(String[] args) {
        A first = new A();
        first.setName("demo");
        first.setIds(Arrays.asList(1, 2, 3));

        B second = new B();
        final BeanCopier beanCopier = BeanCopier.create(A.class, B.class, false);
        beanCopier.copy(first,second,null);

        for (String each : second.getIds()) {// Type conversion exception
            System.out.println(each);
        }
    }
}

Similarly, the problem is exposed at runtime.

Next, let's look at mapstruct:

import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

@Mapper
public interface Converter {
    Converter INSTANCE = Mappers.getMapper(Converter.class);

    B aToB(A car);
}
import java.util.Arrays;

public class BeanUtilDemo {
    public static void main(String[] args) {
        A first = new A();
        first.setName("demo");
        first.setIds(Arrays.asList(1, 2, 3));

        B second = Converter.INSTANCE.aToB(first);
        for (String each : second.getIds()) {// normal
            System.out.println(each);
        }
    }
}

You can successfully convert list < integer > in A to list < string > in B. Let's take A look at the Converter implementation class generated by compilation:

import java.util.ArrayList;
import java.util.List;
import javax.annotation.Generated;
import org.springframework.stereotype.Component;

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    comments = "version: 1.3.1.Final, compiler: javac, environment: Java 1.8.0_202 (Oracle Corporation)"
)
@Component
public class ConverterImpl implements Converter {

    @Override
    public B aToB(A car) {
        if ( car == null ) {
            return null;
        }

        B b = new B();

        b.setName( car.getName() );
        b.setIds( integerListToStringList( car.getIds() ) );

        return b;
    }

    protected List<String> integerListToStringList(List<Integer> list) {
        if ( list == null ) {
            return null;
        }

        List<String> list1 = new ArrayList<String>( list.size() );
        for ( Integer integer : list ) {
            list1.add( String.valueOf( integer ) );
        }

        return list1;
    }
}

Automatic conversion helps us, and we may not realize that the types are inconsistent.

If we add a String number attribute in class A and a Long number attribute in class B, maptrue will be reported when number is set to non numeric type NumberFormatException.

@Override
public B aToB(A car) {
    if ( car == null ) {
        return null;
    }

    B b = new B();

    b.setName( car.getName() );
    if ( car.getNumber() != null ) { // That's the problem
        b.setNumber( Long.parseLong( car.getNumber() ) );
    }
    b.setIds( integerListToStringList( car.getIds() ) );

    return b;
}

If cglib is used, the number attribute will not be mapped by default, and the number in B is null. If you define the converter manually, use the IDEA plug-in (such as generateO2O) to automatically convert:

public final class A2BConverter {

    public static B from(A first) {
        B b = new B();
        b.setName(first.getName());
        b.setIds(first.getIds());
        return b;
    }
}

This problem can be found very clearly in the coding stage:

3 conclusion

Because Java generics are actually checked at compile time and erased after compilation, List < integer > and List < string > are List types at runtime, which can be assigned normally. This makes it difficult to make obvious errors at compile time when using many attribute mapping tools.

mapstruct customizes the annotation processor, which can read the generic types of both sides of the mapping in the compilation stage, and then map. But this mapping is also terrible. Sometimes we define the wrong type due to carelessness and other reasons, and automatically help us with the conversion, which will bring a lot of side effects.

The performance of various attribute mapping tools has been briefly compared before, and the results are as follows:

Therefore, use the attribute conversion tool with caution. If possible, it is recommended to customize the conversion class and use the IDEA plug-in to fill it automatically, which is also very efficient. If any attribute type in A or B does not match, or even delete an attribute, an error can be reported at the compilation stage, and the efficiency of directly calling get set is also very high.

Recent hot article recommendations:

1.1000 + Java interview questions and answers (2022 latest version)

2.Hot! The Java collaboration is coming...

3.Spring Boot 2.x tutorial, too complete!

4.20w programmer red envelope cover, get it quickly...

5.Java development manual (Songshan version) is the latest release. Download it quickly!

Feel good, don't forget to like + forward!

Topics: Java