解决Hutool BeanUtil 拷贝异常场景

这篇具有很好参考价值的文章主要介绍了解决Hutool BeanUtil 拷贝异常场景。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

背景

我们使用的是Hutool工具包的cn.hutool.core.bean.BeanUtil解决对象拷贝复制场景。

工作中我们经常做这样工作:比如说将VO复制成DO。 VO、DTO、DTO、BO,RequestDTO互相转化。

业务

 我们服务作为系统的开放平台应用,统一维护管理第三方平台API接口。比如企业微信接口。而我们使用开源项目 wxJava 方便我们调用企业微信API。 我们需要将wxJava 的接口入参类复制一份作为项目的RequestDTO,做到业务隔离避免其他项目直接依赖。所以牵扯到到大量的对象拷贝工作。

场景

目标类  

WxCpWelcomeMsg

/**
 * 消息文本消息.
 *
 * @author <a href="https://github.com/binarywang">Binary Wang</a>
 * @date 2020-08-16
 */·········        ·
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class WxCpWelcomeMsg implements Serializable {
  private static final long serialVersionUID = 4170843890468921757L;

  @SerializedName("welcome_code")
  private String welcomeCode;

  private Text text;

  private List<Attachment> attachments;

  public String toJson() {
    return WxCpGsonBuilder.create().toJson(this);
  }
}
/**
 * 消息文本消息.
 *
 * @author <a href="https://github.com/binarywang">Binary Wang</a>
 * @date 2020-08-16
 */
@Data
@Accessors(chain = true)
public class Text implements Serializable {
  private static final long serialVersionUID = 6608288753719551600L;
  private String content;
}
package me.chanjar.weixin.cp.bean.external.msg;

import com.google.gson.annotations.SerializedName;
import lombok.Data;
import me.chanjar.weixin.cp.constant.WxCpConsts;

import java.io.Serializable;

/**
 * @author chutian0124
 */
@Data
public class Attachment implements Serializable {
  private static final long serialVersionUID = -8078748379570640198L;

  @SerializedName("msgtype")
  private String msgType;

  private Image image;

  private Link link;

  @SerializedName("miniprogram")
  private MiniProgram miniProgram;

  private Video video;

  private File file;

  public void setImage(Image image) {
    this.image = image;
    this.msgType = WxCpConsts.WelcomeMsgType.IMAGE;
  }

  public void setLink(Link link) {
    this.link = link;
    this.msgType = WxCpConsts.WelcomeMsgType.LINK;
  }

  public void setMiniProgram(MiniProgram miniProgram) {
    this.miniProgram = miniProgram;
    this.msgType = WxCpConsts.WelcomeMsgType.MINIPROGRAM;
  }

  public void setVideo(Video video) {
    this.video = video;
    this.msgType = WxCpConsts.WelcomeMsgType.VIDEO;
  }

  public void setFile(File file) {
    this.file = file;
    this.msgType = WxCpConsts.WelcomeMsgType.FILE;
  }
}
/**
 * 图片消息.
 *
 * @author <a href="https://github.com/binarywang">Binary Wang</a>
 * @date 2020-08-16
 */
@Data
public class Image implements Serializable {
  private static final long serialVersionUID = -606286372867787121L;

  @SerializedName("media_id")
  private String mediaId;

  @SerializedName("pic_url")
  private String picUrl;
}


/**
 * 图文消息.
 *
 * @author <a href="https://github.com/binarywang">Binary Wang</a>
 * @date 2020-08-16
 */
@Data
public class Link implements Serializable {
  private static final long serialVersionUID = -8041816740881163875L;
  private String title;
  @SerializedName("picurl")
  private String picUrl;
  private String desc;
  private String url;
  @SerializedName("media_id")
  private String mediaId;
}
/**
 * 小程序消息.
 *
 * @author <a href="https://github.com/binarywang">Binary Wang</a>
 * @date 2020-08-16
 */
@Data
public class MiniProgram implements Serializable {
  private static final long serialVersionUID = 4242074162638170679L;

  private String title;
  @SerializedName("pic_media_id")
  private String picMediaId;
  private String appid;
  private String page;
}
/**
 * 视频消息
 *
 * @author pg
 * @date 2021-6-21
 */
@Data
public class Video implements Serializable {
  private static final long serialVersionUID = -6048642921382867138L;
  @SerializedName("media_id")
  private String mediaId;
  @SerializedName("thumb_media_id")
  private String thumbMediaId;
}
/**
 * @author <a href="https://github.com/binarywang">Binary Wang</a>
 * @date 2021-08-23
 */
@Data
public class File implements Serializable {
  private static final long serialVersionUID = 2794189478198329090L;

  @SerializedName("media_id")
  private String mediaId;
}





来源对象

WxCpWelcomeMsg 是我们自定义RequestDTO 

读取数据

 String content = "{\"attachments\":[{\"image\":{},\"msgType\":\"image\"}],\"platformCode\":\"corp_wx\",\"responseClass\":\"java.lang.Void\",\"responseType\":\"java.lang.Void\",\"text\":{\"content\":\"22\"},\"welcomeCode\":\"Eu8O9rXwWoaPRTXGmNT-F1_aDQevOWjI6FyVEyBnZLk\",\"wxApiEnum\":\"ExternalContact\"}";

WxCpWelcomeMsgRequest request = JSON.parseObject(content, WxCpWelcomeMsgRequest.class);

Hutool BeanUtil.copyProperties 拷贝对象有bug

代码如下

BeanUtil.copyProperties(request,WxCpWelcomeMsg.class);

结果

cn.hutool.core.bean.beanutil,java

异常

msgType值竟然是file ,不是image。这是什么奇葩现象!

debug 探索问题

现象

cn.hutool.core.bean.beanutil,java

 cn.hutool.core.bean.beanutil,java

 BeanUtil工具会调用目标类每个setter方法,哪怕入参是null,导致msgType等于file

尝试解决


WxCpWelcomeMsg wxCpWelcomeMsg1 = new WxCpWelcomeMsg();
BeanUtil.copyProperties(request, wxCpWelcomeMsg1,CopyOptions.create().setIgnoreNullValue(true));

配置拷贝策略,忽略null但还是不能解决。

过程我就不贴出来。直接给出最终定位的方法

	/**
	 * 转换值为指定类型
	 *
	 * @param <T>           转换的目标类型(转换器转换到的类型)
	 * @param type          类型目标
	 * @param value         被转换值
	 * @param defaultValue  默认值
	 * @param isCustomFirst 是否自定义转换器优先
	 * @return 转换后的值
	 * @throws ConvertException 转换器不存在
	 */
	@SuppressWarnings("unchecked")
	public <T> T convert(Type type, Object value, T defaultValue, boolean isCustomFirst) throws ConvertException {
		if (TypeUtil.isUnknown(type) && null == defaultValue) {
			// 对于用户不指定目标类型的情况,返回原值
			return (T) value;
		}
		if (ObjectUtil.isNull(value)) {
			return defaultValue;
		}
		if (TypeUtil.isUnknown(type)) {
			type = defaultValue.getClass();
		}

		if (type instanceof TypeReference) {
			type = ((TypeReference<?>) type).getType();
		}

		// 标准转换器
		final Converter<T> converter = getConverter(type, isCustomFirst);
		if (null != converter) {
			return converter.convert(value, defaultValue);
		}

		Class<T> rowType = (Class<T>) TypeUtil.getClass(type);
		if (null == rowType) {
			if (null != defaultValue) {
				rowType = (Class<T>) defaultValue.getClass();
			} else {
				// 无法识别的泛型类型,按照Object处理
				return (T) value;
			}
		}

		// 特殊类型转换,包括Collection、Map、强转、Array等
		final T result = convertSpecial(type, rowType, value, defaultValue);
		if (null != result) {
			return result;
		}

		// 尝试转Bean
		if (BeanUtil.isBean(rowType)) {
			return new BeanConverter<T>(type).convert(value, defaultValue);
		}

		// 无法转换
		throw new ConvertException("Can not Converter from [{}] to [{}]", value.getClass().getName(), type.getTypeName());
	}
	// 尝试转Bean
	if (BeanUtil.isBean(rowType)) {
		return new BeanConverter<T>(type).convert(value, defaultValue);
	}
	/**
	 * 构造,默认转换选项,注入失败的字段忽略
	 *
	 * @param beanType 转换成的目标Bean类型
	 */
	public BeanConverter(Type beanType) {
		this(beanType, CopyOptions.create().setIgnoreError(true));
	}

这块使用new BeanConverter(type) .构造器。 没有调用使用者传入的CopyOptions拷贝选项。

看起来Hutool 这块设计比较差!

使用BeanCopier 方案

WxCpWelcomeMsg wxCpWelcomeMsg = new WxCpWelcomeMsg();
BeanCopier beanCopier = BeanCopier.create(WxCpWelcomeMsgRequest.class, WxCpWelcomeMsg.class, false);
        beanCopier.copy(request,wxCpWelcomeMsg,null);

结果

cn.hutool.core.bean.beanutil,java

异常

text没有赋值 

尝试解决

由于Text类定义使用Accessors 注解

/**
 * 消息文本消息.
 *
 * @author <a href="https://github.com/binarywang">Binary Wang</a>
 * @date 2020-08-16
 */
@Data
@Accessors(chain = true)
public class Text implements Serializable {
  private static final long serialVersionUID = 6608288753719551600L;
  private String content;
}

翻看beanCopirer源码,无法获取包含返回值不为void的set方法。

使用Orika 方案

DefaultMapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
mapperFactory.registerFilter(new MyFilter<>());
mapperFactory.getMapperFacade().map(request,wxCpWelcomeMsg);
/**
 * 配置过滤器,若入参对象是空,则不注入
 **/
public class MyFilter<A,B>  extends NullFilter<A,B> {

    @Override
    public <S extends A, D extends B> boolean shouldMap(Type<S> sourceType, String sourceName, S source, Type<D> destType, String destName, D dest, MappingContext mappingContext) {
        return source != null;
    }
}

结果

cn.hutool.core.bean.beanutil,java

按预期结果拷贝参数。

结论

 Orika 组件是兼容Lombok的Accessors 配置的。文章来源地址https://www.toymoban.com/news/detail-770815.html

到了这里,关于解决Hutool BeanUtil 拷贝异常场景的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Hutool BeanUtil工具使用及SQL中AND与OR优先级解析

    本文深入探讨了Hutool工具包中的BeanUtil工具的使用,以及SQL查询语句中AND和OR逻辑运算符的优先级问题。

    2024年02月10日
    浏览(61)
  • cn.hutool.poi.excel.ExcelWriter 基础操作

    创建ExcelWriter 表格合并操作 行样式设置 写入列表数据 指定单元格式写入数据 结束 

    2024年02月13日
    浏览(48)
  • HttpClient报错cn.hutool.http.HttpException: Read timed out

    这个错误出现是一个项目更新以后的出现的这个问题,错误的代码如下: 这里解释一下,因为项目使用了hutool这个工具包,并且http请求也是用的hutool包中的HttpRequest,但是其实hutool中的HttpRequest是对httpClient做了封装本质上也是HttpClient报的错 接下来说一下原因:需求是这样的

    2024年02月02日
    浏览(116)
  • cn.hutool.http.HttpUtil类get方法不支持获取重定向地址(避坑)

    问题 工作中场景需要通过获取url地址内容,展示返回给客户端,但上线后发现不满足需求,原因是url地址进行302重定向, 进一步了解是因为HttpUtil.get方法不能获取重定向地址,需要使用HttpUtil.createGet()来设置打开重定; 理解302: 302 表示临时性重定向,访问一个Url时,被重定

    2024年02月01日
    浏览(42)
  • 【解决问题思路分析】记录hutool默认使用服务端上次返回cookie的问题解决思路

    本服务需要调用第三方接口获取数据,首先调用public-key接口获取公钥,然后用公钥加密密码,将用户名和密码传入/ticket接口,获取Cookie和response body中的token。 由于是调用第三方接口出现问题,第一步先拉通第三方对接人查看后台日志,对方使用apisix作为api网关,初步判断是

    2024年02月07日
    浏览(48)
  • 使用HuTool的Http工具发送post传递中文参数,请求会乱码的解决方法

    Hutool 是一款功能丰富、易用的Java工具类库,我们在工作中经常会使用它的各种类库方法简化我们的开发,其中我们甚至能通过它的Http工具类直接发送http的各种请求,下面来介绍一下发送post请求时参数中有中文出现乱码的解决方法。 这样就不会造成请求参数出现乱码问题

    2024年02月01日
    浏览(43)
  • 【Java 工具类 Hutool】项目中如何引入 Hutool 并正确使用它

    👉 博主介绍 : 博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家,WEB架构师,阿里云专家博主,华为云云享专家,51CTO 专家博主 ⛪️ 个人社区 :个人社区 💞 个人主页 :个人主页 🙉 初级进阶地址 : ✅ Java 初级进阶路线 🙉 八股文专题 :

    2024年02月03日
    浏览(47)
  • 基于hutool的MD5加密以及hutool的一些常用工具

    直接上干货! 第一步:添加依赖 maven 在项目的pom.xml的dependencies中加入以下内容: 如果不是maven项目,需要去官网下载jar包,安装到项目中, hutool官网:hutool官网地址 第二步:使用 第一种:String encode = DigestUtil.md5Hex(“abcdefg”); 第二种:String encode =SecureUtil.md5(“abcdefg”); 其它

    2024年03月27日
    浏览(60)
  • Java:Hutool工具箱之Hutool-crypto加密解密

    文档 https://hutool.cn/docs/#/crypto/概述 重点单词: 摘自文档 依赖 以MD5 为例 以AES 加密为例 这里有个问题,如果秘钥长度不够16位,会报错 长度只能是16位,24位,32位 参考 https://toscode.gitee.com/dromara/hutool/issues/I4O1EB 以RSA为例

    2024年02月16日
    浏览(85)
  • hutool工具包 中的雪花算法Snowflake 获取Long类型id 或者String 类型id(全局唯一id解决方案)

    1.引入pom依赖 2.源码 3. 注入 使用 4优缺点:

    2024年02月14日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包