接口超时日志排查分析-BeanUtils对象复制6秒及类型不一致复制异常,复制null属性被覆盖解决,常见Bean拷贝框架的性能对比

接口超时日志排查分析-BeanUtils对象复制6秒及类型不一致复制异常,复制null属性被覆盖解决,常见Bean拷贝框架的性能对比

1.接口超时日志排查分析-BeanUtils对象复制6秒
1.查询日志命令,分析接口的请求及响应的时长
cat proJectDock.log | grep -E "请求开始时间|请求正常消耗时间" >> dock1.log

2.org.springframework.beans.BeanUtils
org.springframework.beans.BeanUtils 通常用于Java Spring框架中的对象拷贝。如果你在使用 BeanUtils 花费了6秒钟,这可能是因为以下几个原因:
2.1 拷贝的对象太大,包含大量的属性,导致处理时间较长。
2.2 被拷贝的对象中可能包含复杂的数据结构或者循环引用,导致递归时间增加。
2.3 如果涉及到JDBC等数据库操作,可能是查询数据的时间较长。

3.本地项目中,将日志打印的json串复制到本地测试类,json转对象,然后对象复制。未复现。
BeanUtils.copyProperties(req,orderReqVO);
请求 req= json串

4.类型不一致复制异常,复制null属性被覆盖解决demo

package com.example.core.mydemo.BeanUtils;

import java.util.Date;
import java.util.List;

public class A {
    private String name;
    private List<Integer> ids;
    private Integer age;
    private Date regDate;

    public Date getRegDate() {
        return regDate;
    }

    public void setRegDate(Date regDate) {
        this.regDate = regDate;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Integer> getIds() {
        return ids;
    }

    public void setIds(List<Integer> ids) {
        this.ids = ids;
    }
}


package com.example.core.mydemo.BeanUtils;

import java.util.Date;
import java.util.List;

public class B {
    private String name;
    private List<String> ids;
    private Integer age;
    private Date regDate;

    public Date getRegDate() {
        return regDate;
    }

    public void setRegDate(Date regDate) {
        this.regDate = regDate;
    }
    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<String> getIds() {
        return ids;
    }

    public void setIds(List<String> ids) {
        this.ids = ids;
    }
}



package com.example.core.mydemo.BeanUtils;

import com.alibaba.fastjson.JSON;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.cglib.beans.BeanCopier;

import java.util.*;

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

        B second = new B();
        second.setName("优质");
//        second.setAge(10);

        /**
         * //存在的问题:
         * //会将源对象中字段为null的值,覆盖到目标有 值字段。最终目标对象中的对象值被覆盖,也为null
         * second={"regDate":1733455040911}
         * second date=Fri Dec 06 11:17:20 CST 2024
         */
        BeanUtils.copyProperties(first, second);
        System.out.println("second=" + JSON.toJSONString(second));
        System.out.println("second date=" + second.getRegDate());

        second.setName("优质");
        /**
         * 解决null被覆盖的情况 - "ignoreProperties"
         * str数组=[Ljava.lang.String;@7e0ea639
         * second 非空={"name":"优质","regDate":1733454983304}
         */
        BeanUtils.copyProperties(first, second, getNullPropertyNames(first));
        System.out.println("second 非空=" + JSON.toJSONString(second));

        /**
         * Exception in thread "main" java.lang.NullPointerException
         *     at com.example.core.mydemo.BeanUtils.BeanUtilDemo.main(BeanUtilDemo.java:16)
         */
        BeanUtils.copyProperties(first, second);
        for (String each : second.getIds()) {// 类型转换异常
            System.out.println(each);
        }


        /**
         * Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
         *     at com.example.core.mydemo.BeanUtils.BeanUtilDemo.main(BeanUtilDemo.java:23)
         */
//        final BeanCopier beanCopier = BeanCopier.create(A.class, B.class, false);
//        beanCopier.copy(first,second,null);
//
//        for (String each : second.getIds()) {// 类型转换异常
//            System.out.println(each);
//        }

    }

    public static String[] getNullPropertyNames (Object source) {
        final BeanWrapper src = new BeanWrapperImpl(source);
        java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();

        Set<String> emptyNames = new HashSet<String>();
        for(java.beans.PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if(srcValue == null) {
                emptyNames.add(pd.getName());
            }
        }
        String[] result = new String[emptyNames.size()];
        String[]  str = emptyNames.toArray(result);
        System.out.println("str数组=" + str);
        return str;
    }

}

工具类都是对bean之间存在属性名相同的属性进行处理,无论是源bean或者是目标bean中多出来的属性均不处理。
BeanUtils对部分属性不支持null,具体如下:
a. java.util.Date类型不支持,但是它的子类java.sql.Date是被支持的。java.util.Date直接copy会报异常; >> 验证不会
b. Boolean,Integer,Long等不支持,会将null转化为0; >> 验证不会
c. String支持,转化后依然为null。 >>验证会

BeanUtils支持的转换类型如下
* java.lang.BigDecimal
* java.lang.BigInteger
* boolean and java.lang.Boolean
* byte and java.lang.Byte
* char and java.lang.Character
* java.lang.Class
* double and java.lang.Double
* float and java.lang.Float
* int and java.lang.Integer
* long and java.lang.Long
* short and java.lang.Short
* java.lang.String
* java.sql.Date
* java.sql.Time
* java.sql.Timestamp

3.常见Bean拷贝框架的性能对比
由于 Java 的泛型其实是编译期检查,编译后泛型擦除,导致运行时 List<Integer> 和 List<String> 都是 List 类型,可以正常赋值。这就导致在使用很多属性映射工具时,编译时不容易明显的错误。
因此慎用属性转换工具,如果可能建议自定义转换类,使用 IDEA插件自动填充,效率也挺高, A 或 B 中任何属性类型不匹配,甚至删除一个属性,编译阶段即可报错,而且直接调用 get set 的效率也是非常高的。

1.apache BeanUtils 阿里规范中,明确说明了,不要使用它

<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.4</version>
</dependency>
//关键方法
BeanUtils.copyProperties(res, source);

2.cglib BeanCopier cglib是通过动态代理的方式来实现属性拷贝的,与上面基于反射实现方式存在本质上的区别,这也是它性能更优秀的主因

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.2.8.RELEASE</version>
    <scope>compile</scope>
</dependency>
或者
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
//关键方法
BeanCopier copier = BeanCopier.create(source.getClass(), target, false);
copier.copy(source, res, null);

3.spring BeanUtils 基于内省+反射,借助getter/setter方法实现属性拷贝,性能比apache高

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.2.1.RELEASE</version>
    <scope>compile</scope>
</dependency>
//关键方法
BeanUtils.copyProperties(source, res);

4. hutool BeanUtil hutool 提供了很多的java工具类,从测试效果来看它的性能比apache会高一点,当低于spring

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-core</artifactId>
    <version>5.6.0</version>
</dependency>
//关键方法
BeanUtil.toBean(source, target);

5. MapStruct MapStruct 性能更强悍了,缺点也比较明显,需要声明bean的转换接口,自动代码生成的方式来实现拷贝,性能媲美直接的get/set

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.4.2.Final</version>
</dependency>
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.4.2.Final</version>
</dependency>
//关键方法
@Mapper    //声明bean的转换接口
public interface MapStructCopier {
    Target copy(Source source);
}

@Component
public class MapsCopier {
    private MapStructCopier mapStructCopier = Mappers.getMapper(MapStructCopier.class);

    public Target copy(Source source, Class<Target> target) {
        return mapStructCopier.copy(source);
    }
}

 

posted on 2024-12-09 18:54  oktokeep  阅读(14)  评论(0编辑  收藏  举报