常见场景

我们如果有两个具有很多相同属性名的JavaBean对象a和b,想把a中的属性赋值到b,例如

接口中将接收到的前端请求参数XxxReqVo,我们想把这个入参转化为XxxQuery对象作为数据库的查询条件对象
传统做法是手动set,即

1
2
3
4
5
XxxQuery xxxQuery = new XxxQuery();

xxxQuery .setAxx(xxxReqVo.getAxx());
xxxQuery .setBxx(xxxReqVo.getBxx());
xxxQuery .setCxx(xxxReqVo.getCxx());

如果有几十个需要赋值的的字段呢?那就很头疼了org.springframework.beans.BeanUtils,它提供了对java反射和自省API的包装。它里面还有很多工具类,这里我们介绍一下该类下面的copyProperties方法,该工具方法可以帮我们大大简化这一步

1
2
3
4
5
6
7
8
9
@Data
public class User {
private String id;
private String name;
private String age;
private String account;
private String password;
}

1
2
3
4
5
6
7
8
@Data
public class Person {
private String id;
private String name;
private String age;
private String sex;
}

测试数据

1
2
3
4
5
6
7
8
9
10
11
12
public class Test {
public static void main(String[] args) {
User user = new User();
user.setId("1");
user.setAge("2");
user.setName("wzh");
user.setAccount("wangzh");
user.setPassword("1111");
Person person = new Person();
BeanUtils.copyProperties(user,person);
}

实验结果

1
Person(id=1, name=wzh, age=2, sex=null)

结论

1
BeanUtils.copyProperties(source,target);
  • 源对象source的属性拷贝值赋给目标对象target的过程中,属性名和属性类型都相同的属性才能被成功拷贝赋值,例如id,name,age这三个目标对象的属性被赋值成功,目标对象中的sex属性,由于源对象中没有同名的属性所以没法被赋值成功。
  • 做赋值的属性一定要有对应的setter/getter才能成功赋值

利用忽略值进行合并

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
@Data
public class WxLoginResp {

/**
*
* @author wfs
* @date 2023/9/26
*/
private String sessionKey;

/**
* 用户id
* @author wfs
* @date 2023/9/26
*/
private String openId;

private String nickname;

private String avatarUrl;

private String gender;

@JsonSerialize(using=ToStringSerializer.class)
private Integer isVolunteer;

@JsonSerialize(using= ToStringSerializer.class)
private Long userId;

@JsonSerialize(using= ToStringSerializer.class)
private Long volunteerId;
}

wxLoginResp1

1
2
3
WxLoginResp wxLoginResp1 = new WxLoginResp();
wxLoginResp1.setOpenId("dkhgkdg");
wxLoginResp1.setSessionKey("dkldgaslg");

wxLoginResp2

1
2
3
4
WxLoginResp wxLoginResp2 = new WxLoginResp();
wxLoginResp2.setNickname("dkhgkdg");
wxLoginResp2.setIsVolunteer("dkldgaslg");
...除wxLoginResp1赋值过的的其他值都赋值
  • 接下来如果我将wxLoginResp2的值赋值给wxLoginResp1BeanUtils.copyProperties(wxLoginResp2,wxLoginResp2);

  • 那么wxLoginResp1当中的,openId和SessionKey的值是NULL。

  • 因为在wxLoginResp2当中,openId和SessionKey没有被赋值啊,所以过去肯定是NULL

  • 这个时候,其实我们可以自己新建一个类,这个类只有openId和SessionKey的属性,在将这个类重新转化给wxLoginResp2,但是太冗余了

于是我们引进ignore,赋值时忽略某些属性

1
2
3
4
String[] ignore = {"sessionKey","openId"};
BeanUtils.copyProperties(wxLoginResp1, wxLoginResp,ignore);
log.info("wxLoginResp1_{}",wxLoginResp1);
return Result.ok(wxLoginResp);

封装了又一个类

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
150
151
152
153
154
155
156
package com.example.fresh_demo.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.converters.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
* @version: 1.00.00
* @description: 相同字段类之间转换工具类
* @date: 2018/7/27
* @history:
*/
@Slf4j
public class ConverterUtils {
private static final Logger LOG = LoggerFactory.getLogger(ConverterUtils.class);

/**
* @param po
* @param clazz DTO.class
* @return : T
* @Description : 单个 po转dto
* @Author : youwenfeng
* @Date : 2018/7/27
*/
public static <FROM, TO> TO po2dto(FROM po, Class<TO> clazz) {
return converte(po, clazz);
}

/**
* @param poList
* @param clazz DTO.class
* @return : java.util.List<T>
* @Description : 批量 po转dto
* @Author : youwenfeng
* @Date : 2018/7/27
*/
public static <FROM, TO> List<TO> po2dto(List<FROM> poList, Class<TO> clazz) {
return converte(poList, clazz);
}

/**
* @param dto
* @param clazz PO.class
* @return : T
* @Description : 单个 dto转po
* @Author : youwenfeng
* @Date : 2018/7/27
*/
public static <FROM, TO> TO dto2po(FROM dto, Class<TO> clazz) {
return converte(dto, clazz);
}

/**
* @param dtoList
* @param clazz PO.class
* @return : java.util.List<T>
* @Description : 批量 dto转po
* @Author : youwenfeng
* @Date : 2018/7/27
*/
public static <FROM, TO> List<TO> dto2po(List<FROM> dtoList, Class<TO> clazz) {
return converte(dtoList, clazz);
}


/**
* @param from
* @param clazz
* @return : T
* @Description : 单转换
* @Author : youwenfeng
* @Date : 2018/7/27
*/
public static <FROM, TO> TO converte(FROM from, Class<TO> clazz) {
TO to = null;
if (null != from) {
try {
to = clazz.newInstance();
org.apache.commons.beanutils.ConvertUtils.register(new DateConverter(null), java.util.Date.class);
org.apache.commons.beanutils.ConvertUtils.register(new LongConverter(null), Long.class);
org.apache.commons.beanutils.ConvertUtils.register(new ShortConverter(null), Short.class);
org.apache.commons.beanutils.ConvertUtils.register(new IntegerConverter(null), Integer.class);
org.apache.commons.beanutils.ConvertUtils.register(new DoubleConverter(null), Double.class);
org.apache.commons.beanutils.ConvertUtils.register(new BigDecimalConverter(null), BigDecimal.class);
ConvertUtils.register(new BooleanConverter(null), Boolean.class);
BeanUtils.copyProperties(from, to);
} catch (IllegalAccessException e) {
LOG.error("IllegalAccessException: ", e.getMessage());
} catch (InstantiationException e) {
LOG.error("InstantiationException: ", e.getMessage());
}
}
return to;
}

/**
* @param fromList
* @param clazz
* @return : java.util.List<T>
* @Description : 批量转换
* @Author : youwenfeng
* @Date : 2018/7/27
*/
public static <FROM, TO> List<TO> converte(List<FROM> fromList, Class<TO> clazz) {
List<TO> toList = new ArrayList<>();
if (null != fromList && !fromList.isEmpty()) {
for (FROM from : fromList) {
TO to = converte(from, clazz);
if (null != to) {
toList.add(to);
}
}
}
return toList;
}

public static List<String> Long2String(List<Long> list) {
if (null == list) {
return null;
}
List<String> stringList = new ArrayList<>();
if (!list.isEmpty()) {
for (Long l : list) {
stringList.add(String.valueOf(l));
}
}
return stringList;
}

// /**
// * 将poList集合数据转换为dtoList集合数据
// * 支持将数字字符串转换为Integer类型
// *
// * @param poList
// * @param dtoClass
// * @return
// */
// public static <T> List<T> poListToDtoList(List poList, Class<T> dtoClass) {
// List<T> dtoList = new ArrayList<>();
// poList.stream().forEach(item -> {
// Map<String, Object> map = BeanUtil.beanToMap(item);
// T dto = BeanUtil.mapToBean(map, dtoClass, false);
// dtoList.add(dto);
// });
// return dtoList;
// }


}

这个可以直接通过类的模版信息去转化

1
WxLoginResp wxLoginResp1 = ConverterUtils.converte(user, WxLoginResp.class);

通过MAP转换为集合

1
2
3
4
5

//将Map填充成实体类
VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(values, new VoucherOrder(), true);


两个好办法

1
2
3
4
5
 //2. 按照typeId,将店铺进行分组
Map<Long, List<Shop>> map = shopList.stream().collect(Collectors.groupingBy(Shop::getTypeId));

//将Map填充成实体类
VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(values, new VoucherOrder(), true);

将实体类转换成对应的Map

1
2
3
4
5
6
7
8
9
10
11
  UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
HashMap<String, String > userMap = new HashMap<>();
userMap.put("icon", userDTO.getIcon());
userMap.put("id", String.valueOf(userDTO.getId()));
userMap.put("nickName", userDTO.getNickName());
//高端写法,现在我还学不来,工具类还不太了解,只能自己手动转换类型然后put了
// Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
// CopyOptions.create()
// .setIgnoreNullValue(true)
// .setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));

  1. 变量声明:
1
Map<String, Object> userMap

这行代码声明了一个名为userMap的变量,其类型为Map<String, Object>。这个Map的键是String类型,值是Object类型。

  1. BeanUtil.beanToMap()方法:
    这个方法的作用是将一个Java Bean转换为一个Map。Java Bean通常是一个遵循特定命名约定的普通Java类,例如属性名遵循驼峰命名法,并提供getter和setter方法。
  2. 方法参数:
1
2
3
 `userDTO`: 这是要转换的Java Bean对象。  
`new HashMap<>()`: 这是一个新的HashMap实例,用于存储转换后的键值对。
`CopyOptions.create()...`: 这是一个链式调用,用于创建并配置`CopyOptions`对象。`CopyOptions`对象用于控制Bean到Map的转换过程。
  1. CopyOptions配置:
1
2
3
`.setIgnoreNullValue(true)`: 这个设置表示在转换过程中,如果Bean的某个属性值为null,那么这个属性不会被加入到Map中。  
`.setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString())`:
这是一个Lambda表达式,用于定义字段值的编辑器。在这个例子中,无论字段的原始值是什么类型,它都会被转换为String类型。这是通过调用`fieldValue.toString()`方法实现的。

总结:

这段代码的目的是将一个名为userDTO的Java Bean对象转换为一个Map,其中:

  • 键是Bean的属性名。
  • 值是Bean的属性值,如果属性值为null则不会出现在Map中,否则属性值会被转换为String类型。

最后,转换后的Map被赋值给userMap变量。