今天出现一个线上bug,我在排查代码的时候,两个高开在我身后陪我排查代码,慌的一批,害怕让他们看见不爽的代码,结果还真发现了,然后被屌了,如下
List<StudentB> studentbList = new ArrayList<>();
studentAList.forEach(student->{
StudentB studentB = new StudentB();
studentB.setName(student.getName());
studentB.setAddress(student.getAddress());
studentB.setAge(student.getAge());
studentB.setEmail(student.getEmail());
studentbList.add(studentB);
});
不过,这段代码也确实该屌,写的确实是烂,问我为什么这么写,我说apache 和spring 的BeanUtils存在一些问题,然后问我什么问题,我简单说了一下
apache 中的BeanUtils 太复杂,导致性能太差
spring 中的BeanUtils 存在一些问题,如下(关于这个,这篇文件写的不错https://www.jianshu.com/p/357b55852efc)
-
Spring的BeanUtils的CopyProperties方法需要对应的属性有getter和setter方法;
-
如果存在属性完全相同的内部类,但是不是同一个内部类,即分别属于各自的内部类,则spring会认为属性不同,不会copy;
-
泛型只在编译期起作用,不能依靠泛型来做运行期的限制;
-
最后,spring和apache的copy属性的方法源和目的参数的位置正好相反,所以导包和调用的时候都要注意一下。
然后就是接着另一顿屌
重点来了,两个高开大佬,给我推荐了一个新的框架,hutools(糊涂),这个是官网 https://hutool.cn/
然后我就看了下文档,重新写了一个
public class ObjectCopyUtils {
static {
ConverterRegistry converterRegistry = ConverterRegistry.getInstance();
converterRegistry.putCustom(String.class, StringConvert.class);
}
public static void copyProperties(Object sourceData, Object targetData) {
BeanUtil.copyProperties(sourceData, targetData, true);
}
private static class StringConvert implements Converter<String> {
@Override
public String convert(Object value, String defaultValue) throws IllegalArgumentException {
if (null == value) {
return null;
}
if (value instanceof CharSequence) {
return value.toString();
} else if (ArrayUtil.isArray(value)) {
return ArrayUtil.toString(value);
} else if (CharUtil.isChar(value)) {
//对于ASCII字符使用缓存加速转换,减少空间创建
return CharUtil.toString((char) value);
}
return value.toString();
}
}
}
//第一段代码修改以后
List<StudentB> studentbList = new ArrayList<>();
studentAList.forEach(student->{
StudentB studentB = new StudentB();
ObjectCopyUtils.copyProperties(student,studentB);
studentbList.add(studentB);
});
如上
在调用hutools 的BeanUtil方法时,最终回调了内部类SpringConvert的convert方法,能够根据不通的类型,做出不同的判断
分析:
这里面主要包含这几个类: ConverterRegistry 转换登记中心 StringConvert 字符串类型转换类 BeanUtil bean的工具类 BeanCopier 拷贝执行类
执行过程
1. 首先ObjectCopyUtils 初始化时会初始化一个字符串类型的转化类,并且注册到ConverterRegistry转换注册登记中心,并put到一个Map中,在put的时候还是使用了双重检查锁做并发控制
2. 执行拷贝动作,先调用自定义方法 ObjectCopyUtils的的copyProperties方法,copyProperties调用hutools包内的BeanUtil.copyPerties方法,
BeanUtil.copyPerties 再调用BeanCopier的创建方法,创建BeanCopier实例,最后再调用到copy方法
我刚才的那个类src不是 provider ,也不是map,而dest 也不是map,多以会调用到beanToBean,beanToBean 会带用到valProviderBean,接着会通过BeanValueProvider 调用ConverterRegistry,根据类型查找,是否有对应转换器,如果找到,则执行转换方法,如果找不到,则执行默认转换方法
有一点还需要注意,bean在拷贝时,src和desc 必须都是带get和set方法的,若是不带,则属性必须为public 类型,否则拷贝会有问题