编码技巧——Bean转换工具MapStruct

业务开发过程中,经常会遇到各种实体之类的转换,JSON转换,Map转换,属性赋值填充,这里整理几个常用的工具,提升效率。

(1)Bean转换工具类

在一些通用传参、签名验证等场景,需要将接口Param参数或实体,转成Map类型;

import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * @author A
 * @description javaBean转Map工具类
 * @date 2021/1/16
 */
@Slf4j
public class BeanUtils {

    /**
     * obj转Map
     */
    public static Map convertToMap(Object obj) {
        Map map = Maps.newHashMap();
        try {
            // 获取javaBean的BeanInfo对象
            BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass(), Object.class);
            // 获取属性描述器
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
                // 获取属性名
                String key = propertyDescriptor.getName();
                // 获取该属性的值
                Method readMethod = propertyDescriptor.getReadMethod();
                Object value = readMethod.invoke(obj);
                map.put(key, value);
            }
        } catch (Exception e) {
            log.error("BeanUtils.convertToMap_error! e:{}", e);
        }
        return map;
    }

    /**
     * obj转Map
     */
    public static Map convertToStrMap(Object obj) {
        Map map = Maps.newHashMap();
        try {
            final Map stringObjectMap = convertToMap(obj);
            if (MapUtils.isNotEmpty(stringObjectMap)) {
                stringObjectMap.forEach((k, v) -> map.put(k, (String) (v)));
            }
        } catch (Exception e) {
            log.error("BeanUtils.convertToStrMap_error! e:{}", e);
        }
        return map;
    }
}

(2)实体转换

DO、DTO、BO、VO之间的转换,大部分都是同名属性赋值;这里推荐一个导师建议的第三方工具包MapStruct,导师说之前在天猫时使用较多的就是这个插件;原理是扫描并解析注解参数,在编译期生成setter方法;相对BeanUtil的copyProperties,这个插件更为强大和省心,如能自定义setter方法加入业务逻辑,能指定哪些字段需要赋值那些不需要,赋值前后的映射关系,这些都可以通过注解属性实现;而SpringFramework自带的bean转换工具反而不是很推荐,不灵活切可能遇到类转换错误、装箱/拆箱时的空指针等;

依赖:

1.2.0.Final



    org.mapstruct
    mapstruct
    ${mapstruct.version}
    compile


    org.mapstruct
    mapstruct-processor
    ${mapstruct.version}
    compile

新建Mapper接口:

/**
 * bean转换服务
 *
 * @Author AA
 * @CreateDate 2019/12/09
 */
@Mapper(componentModel = "spring")
public interface BeanConvertMapper {

    AccountInfoDTO convertToAccountInfoDTO(AccountInfo accountInfo);

    @Mappings({
            @Mapping(source = "userId", target = "openid")
    })
    UserInfoDTO convertToUserInfo(UserInfo userInfo);

    @Mappings({
            @Mapping(target = "periods", expression = "java(com.xx.internet.supermembercore.service.help.BeanMapperUtil.cfgToPeriods(memberDO))")
    })
    MemberDTO convertToMemberDTO(MemberDO memberDO);

	/*一对一*/
    @Mappings({
            @Mapping(target = "userBenefitId", source = "benefitInstanceId"),
            @Mapping(target = "amount", ignore = true),
            @Mapping(target = "balance", source = "benefitValue"),
            @Mapping(target = "endTime", source = "benefitExpireTime"),
            @Mapping(target = "useStatus", source = "benefitStatus"),
            @Mapping(target = "rcvTime", source = "sendDay")
    })
    UserBenefitDTO convertToUserBenefitDTO(MemberBenefitDO memberBenefitDO);

	/*多对一*/
    @Mappings({
            @Mapping(source = "sku.id", target = "skuId"),
            @Mapping(source = "sku.code", target = "skuCode"),
            @Mapping(source = "sku.price", target = "skuPrice"),
            @Mapping(source = "item.id", target = "itemId"),
            @Mapping(source = "item.title", target = "itemName")
    })
    SkuDTO domain2dto(Item item, Sku sku);

}

参考:

mapstruct使用详解

MapStruct – Java bean mappings, the easy way!

(3)JSON相关的注解

名称到属性的映射、复用类但需要忽略非必要参数给前端;

1. 返回参数时忽略属性,会用到Jackson相关的注解,其中用得较多的是@JSONIgnore:

    (1) @JsonIgnoreProperties
    // 此注解是类注解,作用是json序列化时将java bean中的一些属性忽略掉;
    // 写法将此标签加在model类的类名上,可以多个属性也可以单个属性;
 
    // 生成json时将name和age属性过滤
    @JsonIgnoreProperties({"name"}, {"age"})
    public class user {
        private Long id;
        private String name;
        private int age;
    }

    (2) @JsonIgnore
    // 此注解用于属性或者方法上(最好是属性上),作用和上面的@JsonIgnoreProperties一样

    // 生成json时不生成age属性
    public class user {
        private String name;
        
        @JsonIgnore
        private int age;
    } 

    (3) @JsonFormat
    // 此注解用于属性或者方法上(最好是属性上),可以方便的把Date类型直接转化为我们想要的模式,
    // 比如@JsonFormat(pattern = “yyyy-MM-dd HH-mm-ss”)

    (4) @JsonSerialize
    // 此注解用于属性或者getter方法上,用于在序列化时嵌入我们自定义的代码,比如序列化一个double时在其后面限制两位小数点。

    (5) @JsonDeserialize
    // 此注解用于属性或者setter方法上,用于在反序列化时可以嵌入我们自定义的代码,类似于上面的@JsonSerialize

2. 传输时传参paramName与实体字段名映射;尤其是设计参数与java关键字重合时,非常方便;

如这里的前段传参的参数名"enum",服务端无法使用enum接收参数(java关键字冲突);

/**
 * QuestionSchemaDTO
 */
@Data
public static class QuestionSchemaDTO {

    /**
     * 问题key 需手动赋值 用来匹配答案表单
     */
    private String key;

    /**
     * 问题标题
     */
    @JSONField(name = "title")
    private String questionName;

    /**
     * 问题输入类型 string-单选/单填空 array-多选
     */
    private String type;

    /**
     * 填空题的沉底提示语
     */
    private String placeholder;

    /**
     * 选项 tip:前端协议enum为空则认为是填空题
     */
    @JSONField(name = "enum")
    private List options;

}

你可能感兴趣的:(代码技巧,java)