接口超时日志排查分析-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); } }