目录

Bean Copy

在软件工程中, a plain old Java object (POJO) 是一个普通的Java 对象,除了 Java 语言规范强制的限制之外,不受任何特殊限制的约束。

JavaBeans 是可序列化的POJO ,具有无参数构造函数,并允许使用遵循简单命名约定的getter 和 setter 方法访问属性。由于这种约定,可以对任意 JavaBean 的属性进行简单的声明性引用。使用这种声明性引用的代码不必知道任何关于 bean 的类型,并且 bean 可以与许多框架一起使用,而这些框架不必知道 bean 的确切类型。JavaBeans 规范,如果完全实现的话,会稍微打破 POJO 模型,因为类必须实现Serializable接口是一个真正的JavaBean。许多仍称为 JavaBeans 的 POJO 类不满足此要求。由于Serializable是一个标记(无方法)接口,因此这不是什么负担。

POJO 的定义是无规则简单的对象,在日常的代码分层中 pojo 会被分为VO、BO、 PO、 DTO

VO (view object/value object)表示层对象

  • 前端展示的数据,在接口数据返回给前端的时候需要转成VO

  • 个人理解使用场景,接口层服务中,将DTO转成VO,返回给前台

B0(bussines object)业务层对象

  • 主要在服务内部使用的业务对象

  • 可以包含多个对象,可以用于对象的聚合操作

  • 个人理解使用场景,在服务层服务中,由DTO转成BO然后进行业务处理后,转成DTO返回到接口层

PO(persistent object)持久对象

  • 出现位置为数据库数据,用来存储数据库提取的数据

  • 只存储数据,不包含数据操作

  • 个人理解使用场景,在数据库层中,获取的数据库数据存储到PO中,然后转为DTO返回到服务层中

DTO(Data Transfer Object)数据传输对象

  • 在服务间的调用中,传输的数据对象

  • 个人理解,DTO是可以存在于各层服务中(接口、服务、数据库等等)服务间的交互使用DTO来解耦

DO(domain object)领域实体对象

DO 现在主要有两个版本:

  • 阿里巴巴的开发手册中的定义,DO( Data Object)这个等同于上面的PO

  • DD(Domain-Driven Design)领域驱动设计中,DO(Domain Object)这个等同于上面的BO

对象属性拷贝工具

用 Spring 推荐使用框架自带 BeanUtils.copyProperties, 如果想要复杂功能可以引入 mapstruct, 如果追求极致性能还是手写 setter(可以借助IDEA插件 Simple Object Copy)。

  • Spring BeanUtils (copyProperties)
  • Cglib BeanCopier (copyProperties)
  • Apache BeanUtils (copyProperties)
  • Apache PropertyUtils (copyProperties)
  • Dozer
  • mapstruct
  • JSON 序列化 再反序列化

IDEA Plugin Simple Object Copy

  • 定义方法出入参
  • 光标定位方法内,使用快捷键ALT+INSERT(WIN) 、 command + N(mac) ,或者右键鼠标选择Generate,弹出生成选项框后,选择genCopyMethod,代码就生成好了
1
2
3
public static TargetDTO convertoToTargetDTO(Source source) {

}

generateAllSetter,搭配食用更佳。

javabean 与 map 的转换方式

  • 通过ObjectMapper先将bean转换为json,再将json转换为map,但是这种方法比较绕,且效率很低,不推荐使用

  • 通过java反射,获取bean类的属性和值,再转换到map对应的键值对中,这种方法次之,但稍微有点麻烦

  • 通过 net.sf.cglib.beans.BeanMap 类中的方法,这种方式效率极高,它跟第二种方式的区别就是因为使用了缓存,初次创建bean时需要初始化,之后就使用缓存,所以速度极快,经测试,循环bean和map的转换10000次,仅需要300毫秒左右。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
public class BeanMapInter {

  /**
   * 将javabean对象转换为map
   */
  public static <T> Map<String, Object> beanToMap(T bean) {
    Map<String, Object> map = Maps.newHashMap();
    if (bean != null) {
      BeanMap beanMap = BeanMap.create(bean);
      for (Object key : beanMap.keySet()) {
        Object value = beanMap.get(key);
        map.put(key + "", value);
      }
    }
    return map;
  }

  /**
   * 将map转换为javabean对象
   */
  public static <T> T mapToBean(Map<String, Object> map, T bean) {
    if (map != null) {
      BeanMap beanMap = BeanMap.create(bean);
      beanMap.putAll(map);
    }
    return bean;
  }


  /**
   * 内省(Introspector) 是Java 语言对 JavaBean 类属性、事件的一种缺省处理方法。
   * @param clazz
   * @param object
   * @param userName
   * @param <T>
   * @throws Exception
   */
  public static <T> void setProperty(Class<T> clazz, T object, String userName)throws Exception{
    PropertyDescriptor propDesc=new PropertyDescriptor(userName, clazz);
    Method methodSetUserName=propDesc.getWriteMethod();
    methodSetUserName.invoke(object, "wong");
  }

  public static <T> void getProperty(Class<T> clazz, T object, String userName)throws Exception{
    PropertyDescriptor proDescriptor =new PropertyDescriptor(userName, clazz);
    Method methodGetUserName=proDescriptor.getReadMethod();
    Object objUserName=methodGetUserName.invoke(object);
  }


  public static Map<String, Object> bean2Map(Object obj) {
    if (obj == null) {
      return null;
    }
    Map<String, Object> map = new HashMap<>();
    try {
      BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
      PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
      for (PropertyDescriptor property : propertyDescriptors) {
        String key = property.getName();
        // 过滤class属性
        if (!key.equals("class")) {
          // 得到property对应的getter方法
          Method getter = property.getReadMethod();
          Object value = getter.invoke(obj);
          map.put(key, value);
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    return map;
  }

  public <T> Object mapToBean(Class<T> type, Map<String, Object> map) {
    Object obj = null;
    try {
      BeanInfo beanInfo = Introspector.getBeanInfo(type);
      obj = type.newInstance();
      PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
      for (PropertyDescriptor descriptor : propertyDescriptors) {
        String propertyName = descriptor.getName();
        if (map.containsKey(propertyName)) {
          Object value = map.get(propertyName);
          descriptor.getWriteMethod().invoke(obj, value);
        }
      }
    } catch (Exception e) {
      System.out.println("map转实体类出现异常");
    }
    return obj;
  }

}


@Data
public class RowItem {
  private Long id;
  private String name;
  private Integer age;
  private LocalDateTime createTime;
  private BigDecimal amount;

  public void test(){

    Map<String, Function<RowItem, Object>> getMap = new HashMap<>();

    getMap.put("id",RowItem::getId);
    getMap.put("name",RowItem::getName);
    getMap.put("age",RowItem::getAge);
    getMap.put("createTime",RowItem::getCreateTime);
    getMap.put("amount",RowItem::getAmount);

    Map<String, BiConsumer<RowItem, Object>> setMap = new HashMap<>();
    setMap.put("id",(row, o)-> row.setId((Long) o));
    setMap.put("name",(row, o)-> row.setName(String.valueOf(o)));
    setMap.put("age",(row, o)-> row.setAge((Integer) o));
    setMap.put("createTime",(row, o)-> row.setCreateTime((LocalDateTime) o));
    setMap.put("amount",(row, o)-> row.setAmount((BigDecimal) o));


    RowItem rowItem = new RowItem();
    rowItem.setId(1L);
    rowItem.setName("Ethan");
    rowItem.setAge(18);
    rowItem.setCreateTime(LocalDateTime.now());
    rowItem.setAmount(new BigDecimal("999999"));

    Map<String, Object> result = new HashMap<>();
    for (Map.Entry<String, Function<RowItem, Object>> entry : getMap.entrySet()) {
      result.put(entry.getKey(),  entry.getValue().apply(rowItem));
    }

    System.out.println(result);
    result.put("id",2L);
    result.put("name","Wang");
    result.put("age",30);
    result.put("createTime", LocalDateTime.now());
    result.put("amount", new BigDecimal("77777777"));


    RowItem resultRow = new RowItem();
    for (Map.Entry<String, BiConsumer<RowItem, Object>> entry : setMap.entrySet()) {
      entry.getValue().accept(resultRow,result.get(entry.getKey()));
    }

    System.out.println(resultRow);
  }