项目场景:
提示:这里简述项目相关背景:
项目场景:对用户选择的日期区间内,每周 bug数的不同状态的数量统计(如下图)
问题描述
通常我们创建返回对象的类的时候都是提前预设好的固定字段,针对列的数量不固定且字段名不固定该如何处理?
思路
基于cglib、commons-beanutils库实现
- 将原对象和扩展字段封装为字段map
- 基于字段map和原对象创建其子类对象
- 重新将原字段值和扩展字段值赋给子类对象
- 返回子类对象
方案:
- maven依赖
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.3</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.2.4</version>
</dependency>
- 代码实现
- 原始返回对象类
public class BugListVo {
private String projectName;
public String getProjectName() {
return projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
}
- 工具类
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.PropertyUtilsBean;
import org.springframework.cglib.beans.BeanGenerator;
import org.springframework.cglib.beans.BeanMap;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
/**
* @description: 工具类
*/
@Slf4j
public final class PropertyAppender {
private static final class DynamicBean {
private Object target;
private BeanMap beanMap;
private DynamicBean(Class superclass, Map<String, Class> propertyMap) {
this.target = generateBean(superclass, propertyMap);
this.beanMap = BeanMap.create(this.target);
}
private void setValue(String property, Object value) {
beanMap.put(property, value);
}
private Object getValue(String property) {
return beanMap.get(property);
}
private Object getTarget() {
return this.target;
}
/**
* 根据属性生成对象
*/
private Object generateBean(Class superclass, Map<String, Class> propertyMap) {
BeanGenerator generator = new BeanGenerator();
if (null != superclass) {
generator.setSuperclass(superclass);
}
BeanGenerator.addProperties(generator, propertyMap);
return generator.create();
}
}
public static Object generate(Object dest, Map<String, Object> newValueMap)
throws InvocationTargetException, IllegalAccessException {
PropertyUtilsBean propertyUtilsBean = new PropertyUtilsBean();
// 1.获取原对象的字段数组
PropertyDescriptor[] descriptorArr = propertyUtilsBean.getPropertyDescriptors(dest);
// 2.遍历原对象的字段数组,并将其封装到Map
Map<String, Class> oldKeyMap = new HashMap<>(4);
for (PropertyDescriptor it : descriptorArr) {
if (!"class".equalsIgnoreCase(it.getName())) {
oldKeyMap.put(it.getName(), it.getPropertyType());
newValueMap.put(it.getName(), it.getReadMethod().invoke(dest));
}
}
// 3.将扩展字段Map合并到原字段Map中
newValueMap.forEach((k, v) -> oldKeyMap.put(k, v.getClass()));
// 4.根据新的字段组合生成子类对象
DynamicBean dynamicBean = new DynamicBean(dest.getClass(), oldKeyMap);
// 5.放回合并后的属性集合
newValueMap.forEach((k, v) -> {
try {
dynamicBean.setValue(k, v);
} catch (Exception e) {
log.error("动态添加字段【值】出错", e);
}
});
return dynamicBean.getTarget();
}
}
- 测试
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Test {
private static final ObjectMapper MAPPER = new ObjectMapper();
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, JsonProcessingException {
BugListVo bug = new BugListVo();
bug.setProjectName("project_1");
String jsonStr = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(bug);
System.out.println("BugListVo:" + jsonStr);
Map<String, Object> propertiesMap = new HashMap<>(1);
List<String> weeks = Arrays.asList("25年2周", "25年3周", "25年4周", "25年5周", "25年6周", "25年7周");
for (int i = 0; i < weeks.size(); i++) {
propertiesMap.put(weeks.get(i), i);
}
Object obj = PropertyAppender.generate(bug, propertiesMap);
String jsonStr1 = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
System.out.println("动态为BugListVo添加属性之后,bug:" + jsonStr1);
}
}
- 测试结果
BugListVo:{
“projectName” : “project_1”
}
动态为BugListVo添加属性之后,bug:{
“projectName” : “project_1”,
“25年6周” : 4,
“25年2周” : 0,
“25年3周” : 1,
“25年5周” : 3,
“25年4周” : 2,
“25年7周” : 5
}