当前位置: 首页 > news >正文

apache的BeanUtils的Converter被相互污染覆盖问题

问题描述

apache的BeanUtils工具集中用来把map对象转换为java对象的BeanUtils#populate方法会因为单例的原因其转换器Converter被相互污染覆盖问题

maven依赖

<dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils</artifactId><version>1.9.4</version>
</dependency>

方法源码


public static void populate(final Object bean, final Map<String, ? extends Object> properties)throws IllegalAccessException, InvocationTargetException {// 源码此处获取的是单例对象BeanUtilsBean.getInstance().populate(bean, properties);
}

测试场景

场景一

map不存在属性时

测试代码

/*** map没有age属性*/
public static void test1() throws Exception {DemoObj fromObj = new DemoObj();Map<String, Object> map = Maps.newHashMap();map.put("name", "张三");BeanUtils.populate(fromObj, map);System.out.println(Thread.currentThread().getName() + ":没有age属性,转换器不生效:"+ JsonUtil.toStr(fromObj));
}

输出结果

main:没有age属性,转换器不生效:{"age":null,"name":"张三"}

场景二

map存在age属性,自带的转换器默认值为0

测试代码


/*** map存在age属性,自带的转换器默认值为0*/
public static void test2() throws Exception {DemoObj fromObj = new DemoObj();Map<String, Object> map = Maps.newHashMap();map.put("name", "李四");map.put("age", null);BeanUtils.populate(fromObj, map);System.out.println(Thread.currentThread().getName() +":自带的转换器默认值为0:"+JsonUtil.toStr(fromObj));
}

输出结果

main:自带的转换器默认值为0:{"age":0,"name":"李四"}

场景三

静态代码块注入自定义转换器,默认值设置为null

测试代码


static {ConvertUtils.register(new IntegerConverter(null), Integer.class);
}public static void test2() throws Exception {DemoObj fromObj = new DemoObj();Map<String, Object> map = Maps.newHashMap();map.put("name", "李四");map.put("age", null);BeanUtils.populate(fromObj, map);System.out.println(Thread.currentThread().getName() +":转换器默认值为:"+JsonUtil.toStr(fromObj));
}

输出结果

main:转换器默认值为:{"age":null,"name":"李四"}

场景四

在场景三的基础上,我们增加了一个转换器重新注册的函数调用,该函数会重置转换器对象

转换器重置的源代码

    public static void deregister() {ConvertUtilsBean.getInstance().deregister();}// 获取的依旧是单例对象protected static ConvertUtilsBean getInstance() {return BeanUtilsBean.getInstance().getConvertUtils();}public void deregister() {converters.clear();registerPrimitives(false);registerStandard(false, false);registerOther(true);registerArrays(false, 0);register(BigDecimal.class, new BigDecimalConverter());register(BigInteger.class, new BigIntegerConverter());}

测试代码

static {ConvertUtils.register(new IntegerConverter(null), Integer.class);
}public static void test2() throws Exception {DemoObj fromObj = new DemoObj();Map<String, Object> map = Maps.newHashMap();map.put("name", "李四");map.put("age", null);BeanUtils.populate(fromObj, map);System.out.println(Thread.currentThread().getName() +":转换器默认值为:"+JsonUtil.toStr(fromObj));
}public static void test3() throws Exception {DemoObj fromObj = new DemoObj();Map<String, Object> map = Maps.newHashMap();map.put("name", "王五");map.put("age", null);BeanUtils.populate(fromObj, map);System.out.println(Thread.currentThread().getName() + ":age被赋值默认值:"+ JsonUtil.toStr(fromObj));// 重置转换器ConvertUtils.deregister();
}public static void main(String[] args) throws Exception {new Thread(()-> {try {// 睡眠,等test2先跑完Thread.sleep(1000);test2();} catch (Exception e) {throw new RuntimeException(e);}}).start();new Thread(()-> {try {test3();} catch (Exception e) {throw new RuntimeException(e);}}).start();}

输出结果

当前线程执行时,转换器还是静态代码块所注册的默认值为null的转换器,但是在test3执行完后会重置单例的转换器对象Thread-2:age被赋值默认值:{"age":null,"name":"王五"}所以线程睡眠结束后再执行转换时,发现转换器已被污染,转换的属性默认值非预期值Thread-1:转换器默认值为:{"age":0,"name":"李四"}

场景五

在场景四的基础上重新创建一个单例对象,并且该单例提前注册一个自定义默认值为null的的转换器

新增类


public class CustBeanUtils {private static final BeanUtilsBean beanUtilsBean;public CustBeanUtils() {}public static void populate(Object bean, Map<String, ? extends Object> properties) throws Exception {beanUtilsBean.populate(bean, properties);}static {ConvertUtilsBean convertUtilsBean = new ConvertUtilsBean();convertUtilsBean.register(new IntegerConverter(null), Integer.class);beanUtilsBean = new BeanUtilsBean(convertUtilsBean, new PropertyUtilsBean());}
}

测试代码

static {ConvertUtils.register(new IntegerConverter(null), Integer.class);
}public static void test2() throws Exception {DemoObj fromObj = new DemoObj();Map<String, Object> map = Maps.newHashMap();map.put("name", "李四");map.put("age", null);BeanUtils.populate(fromObj, map);System.out.println(Thread.currentThread().getName() +":转换器默认值为:"+JsonUtil.toStr(fromObj));
}public static void test3() throws Exception {DemoObj fromObj = new DemoObj();Map<String, Object> map = Maps.newHashMap();map.put("name", "王五");map.put("age", null);BeanUtils.populate(fromObj, map);System.out.println(Thread.currentThread().getName() + ":age被赋值默认值:"+ JsonUtil.toStr(fromObj));// 重置转换器ConvertUtils.deregister();
}public static void test4() throws Exception {DemoObj fromObj = new DemoObj();Map<String, Object> map = Maps.newHashMap();map.put("name", "赵六");map.put("age", null);CustBeanUtils.populate(fromObj, map);System.out.println(Thread.currentThread().getName() + ":CustBeanUtils 新建了一个BeanUtilsBean,不受其影响,age也不会被赋值默认值0:"+ JsonUtil.toStr(fromObj));
}public static void main(String[] args) throws Exception {new Thread(()-> {try {Thread.sleep(1000);test2();} catch (Exception e) {throw new RuntimeException(e);}}).start();new Thread(()-> {try {test3();} catch (Exception e) {throw new RuntimeException(e);}}).start();new Thread(()-> {try {Thread.sleep(2000);test4();} catch (Exception e) {throw new RuntimeException(e);}}).start();}

输出结果

线程2执行时,转换器还是静态代码块所注册的默认值为null的转换器,但是在test3执行完后会重置单例的转换器对象Thread-2:age被赋值默认值:{"age":null,"name":"王五"}所以线程1睡眠结束后再执行转换时,发现转换器已被污染,转换的属性默认值非预期值Thread-1:转换器默认值为:{"age":0,"name":"李四"}由于线程3用的是一个新的单例对象,所以其转换结果并不受原生的工具集中转换器重置的函数所影响Thread-3:CustBeanUtils 新建了一个BeanUtilsBean,不受其影响,age也不会被赋值默认值0:{"age":null,"name":"赵六"}
http://www.lryc.cn/news/501048.html

相关文章:

  • TCP的“可靠性”(上)
  • 超标量处理器设计笔记(5)虚拟存储器、地址转换、page fault
  • SparkSQL 读写数据攻略:从基础到实战
  • react 使用状态管理调用列表接口渲染列表(包含条件查询,统一使用查询按钮,重置功能),避免重复多次调用接口的方法
  • Stable Audio Open模型部署教程:用AI打造独家节拍,让声音焕发新活力!
  • 加油站-(贪心算法)
  • k8s-持久化存储PV与PVC(1)
  • Linux Red Hat Enterprise
  • 《中型 Vue 项目:挑战与成长》
  • 配置 DNS over HTTPS阻止DNS污染
  • Facebook广告文案流量秘诀
  • 22. 五子棋小游戏
  • fastadmin框架同时使用 阿里云oss和阿里云点播
  • Java-JMX 组件架构即详解
  • unity打包web,发送post请求,获取地址栏参数,解决TypeError:s.replaceAll is not a function
  • java+ssm+mysql校园物品租赁网
  • Spring Boot中实现JPA多数据源配置指南
  • 服务器加固
  • 探索CSS中的背景图片属性,让你的网页更加美观
  • Oracle的打开游标(OPEN_CURSORS)
  • 数值分析—数值积分
  • 克服大规模语言模型限制,构建新的应用方法——LangChain
  • 计算机网络 —— HTTPS 协议
  • React第十七章(useRef)
  • React第十五节useReducer使用详解差异
  • NanoLog起步笔记-5-客户端简要描述
  • Flink:入门介绍
  • 目标跟踪领域经典论文解析
  • 网络编程 | TCP套接字通信及编程实现经验教程
  • SAP导出表结构并保存到Excel 源码程序