⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 blog.csdn.net/qq_35387940/article/details/129167329 「小目标青年」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

前言

平时做一些统计数据,经常从数据库或者是从接口获取出来的数据,单位是跟业务需求不一致的。

  • 比如, 我们拿出来的 分, 实际上要是元
  • 又比如,我们拿到的数据需要 乘以100 返回给前端做 百分比展示
  • 又比如, 千分比转换
  • 又比如,拿出来的金额需要变成 万为单位
  • 又比如,需要保留2位小数
  • ......
  • 等等等等

平时我们怎么搞?

很多时候拿到的是一个数据集合list,就需要去遍历然后根据每个DTO的属性去做相关单位转换。

一直get 完 set ,get 完 set ,get 完 set ,get 完 set ,get 完 set ,人都麻了。

就像这样:

所以,如果通过反射自动匹配出来一些操作转换,是不是就看代码看起来舒服一点,人也轻松一点。

答案:是的

然后,我就搞了。

正文

本篇内容简要:

  1. 初步的封装,通过map去标记需要转换的 类属性字段
  2. 进一步的封装, 配合老朋友自定义注解搞事情

产品:

  • 支付总金额 换成万 为单位, 方便运营统计 ;
  • 那个什么计数,要是百分比的 ;
  • 然后还有一个是千分比;
  • 另外,还有2个要保留2位小数;
  • 还有啊,那个。。。。。。

我:

别说了,喝口水吧。

拿到的数据都在这个DTO里面:

开始封装:

① 初步的封装,通过map去标记需要转换的 类属性字段

思路玩法:

  1. 通过反射拿出字段
  2. 配合传入的转换标记Map 匹配哪些字段需要操作
  3. 然后从map取出相关字段的具体操作是什么,然后执行转换操作
  4. 重新赋值

① 简单弄个枚举,列出现在需求上的转换操作类型

UnitConvertType.java

/**
* @Author : JCccc
* @CreateTime : 2023/01/14
* @Description :
**/
public enum UnitConvertType {

/**
* 精度
*/
R,
/**
* 万元
*/
B,
/**
* 百分
*/
PERCENTAGE,
/**
* 千分
*/
PERMIL
}

② 核心封装的转换函数

UnitConvertUtil.java

import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* @Author : JCccc
* @CreateTime : 2023/01/14
* @Description :
**/
@Slf4j
public class UnitConvertUtil {

public static <T> void unitMapConvert(List<T> list, Map<String, UnitConvertType> propertyMap) {
for (T t : list) {
Field[] declaredFields = t.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
if (propertyMap.keySet().stream().anyMatch(x -> x.equals(declaredField.getName()))) {
try {
declaredField.setAccessible(true);
Object o = declaredField.get(t);
UnitConvertType unitConvertType = propertyMap.get(declaredField.getName());
if (o != null) {
if (unitConvertType.equals(UnitConvertType.PERCENTAGE)) {
BigDecimal bigDecimal = ((BigDecimal) o).multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP);
declaredField.set(t, bigDecimal);
}
if (unitConvertType.equals(UnitConvertType.PERMIL)) {
BigDecimal bigDecimal = ((BigDecimal) o).multiply(new BigDecimal(1000)).setScale(2, BigDecimal.ROUND_HALF_UP);
declaredField.set(t, bigDecimal);
}
if (unitConvertType.equals(UnitConvertType.B)) {
BigDecimal bigDecimal = ((BigDecimal) o).divide(new BigDecimal(10000)).setScale(2, BigDecimal.ROUND_HALF_UP);
declaredField.set(t, bigDecimal);
}
if (unitConvertType.equals(UnitConvertType.R)) {
BigDecimal bigDecimal = ((BigDecimal) o).setScale(2, BigDecimal.ROUND_HALF_UP);
declaredField.set(t, bigDecimal);
}
}
} catch (Exception ex) {
log.error("处理失败");
continue;
}

}
}
}
}

public static void main(String[] args) {

//获取模拟数据
List<MySumReportDTO> list = getMySumReportList();

Map<String, UnitConvertType> map =new HashMap<>();
map.put("payTotalAmount", UnitConvertType.B);
map.put("jcAmountPercentage", UnitConvertType.PERCENTAGE);
map.put("jcCountPermillage", UnitConvertType.PERMIL);
map.put("length", UnitConvertType.R);
map.put("width", UnitConvertType.R);
unitMapConvert(list,map);
System.out.println("通过map标识的自动转换玩法:"+list.toString());

}

private static List<MySumReportDTO> getMySumReportList() {
MySumReportDTO mySumReportDTO = new MySumReportDTO();
mySumReportDTO.setPayTotalAmount(new BigDecimal(1100000));
mySumReportDTO.setJcAmountPercentage(BigDecimal.valueOf(0.695));
mySumReportDTO.setJcCountPermillage(BigDecimal.valueOf(0.7894));
mySumReportDTO.setLength(BigDecimal.valueOf(1300.65112));
mySumReportDTO.setWidth(BigDecimal.valueOf(6522.12344));

MySumReportDTO mySumReportDTO1 = new MySumReportDTO();
mySumReportDTO1.setPayTotalAmount(new BigDecimal(2390000));
mySumReportDTO1.setJcAmountPercentage(BigDecimal.valueOf(0.885));
mySumReportDTO1.setJcCountPermillage(BigDecimal.valueOf(0.2394));
mySumReportDTO1.setLength(BigDecimal.valueOf(1700.64003));
mySumReportDTO1.setWidth(BigDecimal.valueOf(7522.12344));

List<MySumReportDTO> list = new ArrayList<>();

list.add(mySumReportDTO);
list.add(mySumReportDTO1);
return list;
}

}

代码简析:

看看怎么调用的:

public static void main(String[] args) {

//获取模拟数据
List<MySumReportDTO> list = getMySumReportList();
System.out.println("转换前:"+list.toString());
Map<String, UnitConvertType> map =new HashMap<>();
map.put("payTotalAmount", UnitConvertType.B);
map.put("jcAmountPercentage", UnitConvertType.PERCENTAGE);
map.put("jcCountPermillage", UnitConvertType.PERMIL);
map.put("length", UnitConvertType.R);
map.put("width", UnitConvertType.R);
unitMapConvert(list,map);
System.out.println("通过map标识的自动转换玩法:"+list.toString());

}

代码简析:

效果:

整个集合list的 对应字段都自动转换成功(转换逻辑想怎么样就自己在对应if里面调整、拓展):

② 进一步的封装, 配合老朋友自定义注解搞事情

实说实话,第一步的封装程度已经够用了,就是传map标识出来哪些需要转换,对应转换枚举类型是什么。

其实我感觉是够用的。

但是么,为了用起来更加方便,或者说 更加地可拓展, 那么配合自定义注解是更nice的。

开搞。

创建一个自定义注解 ,JcBigDecConvert.java

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* @Author : JCccc
* @CreateTime : 2023/01/14
* @Description :
**/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface JcBigDecConvert {

UnitConvertType name();
}

怎么用?就是在我们的报表DTO里面,去标记字段。

示例:

MyYearSumReportDTO.java

ps:可以看到我们在字段上面使用了自定义注解

import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;

/**
* @Author : JCccc
* @CreateTime : 2023/2/03
* @Description :
**/

@Data
public class MyYearSumReportDTO implements Serializable {
private static final long serialVersionUID = 5285987517581372888L;

//支付总金额
@JcBigDecConvert(name=UnitConvertType.B)
private BigDecimal payTotalAmount;

//jc金额百分比
@JcBigDecConvert(name=UnitConvertType.PERCENTAGE)
private BigDecimal jcAmountPercentage;

//jc计数千分比
@JcBigDecConvert(name=UnitConvertType.PERMIL)
private BigDecimal jcCountPermillage;

//保留2位
@JcBigDecConvert(name=UnitConvertType.R)
private BigDecimal length;

//保留2位
@JcBigDecConvert(name=UnitConvertType.R)
private BigDecimal width;

}

然后针对配合我们的自定义,封一个转换函数,反射获取属性字段,然后解析注解,然后做对应转换操作。

代码:

public static <T> void unitAnnotateConvert(List<T> list) {
for (T t : list) {
Field[] declaredFields = t.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
try {
if (declaredField.getName().equals("serialVersionUID")){
continue;
}
JcBigDecConvert myFieldAnn = declaredField.getAnnotation(JcBigDecConvert.class);
if(Objects.isNull(myFieldAnn)){
continue;
}
UnitConvertType unitConvertType = myFieldAnn.name();
declaredField.setAccessible(true);
Object o = declaredField.get(t);
if (Objects.nonNull(o)) {
if (unitConvertType.equals(UnitConvertType.PERCENTAGE)) {
BigDecimal bigDecimal = ((BigDecimal) o).multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP);
declaredField.set(t, bigDecimal);
}
if (unitConvertType.equals(UnitConvertType.PERMIL)) {
BigDecimal bigDecimal = ((BigDecimal) o).multiply(new BigDecimal(1000)).setScale(2, BigDecimal.ROUND_HALF_UP);
declaredField.set(t, bigDecimal);
}
if (unitConvertType.equals(UnitConvertType.B)) {
BigDecimal bigDecimal = ((BigDecimal) o).divide(new BigDecimal(10000)).setScale(2, BigDecimal.ROUND_HALF_UP);
declaredField.set(t, bigDecimal);
}
if (unitConvertType.equals(UnitConvertType.R)) {
BigDecimal bigDecimal = ((BigDecimal) o).setScale(2, BigDecimal.ROUND_HALF_UP);
declaredField.set(t, bigDecimal);
}
}
} catch (Exception ex) {
log.error("处理失败");
}
}
}
}

写个调用示例看看效果:

public static void main(String[] args) {

List<MyYearSumReportDTO> yearsList = getMyYearSumReportList();
unitAnnotateConvert(yearsList);
System.out.println("通过注解标识的自动转换玩法:"+yearsList.toString());
}

private static List<MyYearSumReportDTO> getMyYearSumReportList() {
MyYearSumReportDTO mySumReportDTO = new MyYearSumReportDTO();
mySumReportDTO.setPayTotalAmount(new BigDecimal(1100000));
mySumReportDTO.setJcAmountPercentage(BigDecimal.valueOf(0.695));
mySumReportDTO.setJcCountPermillage(BigDecimal.valueOf(0.7894));
mySumReportDTO.setLength(BigDecimal.valueOf(1300.65112));
mySumReportDTO.setWidth(BigDecimal.valueOf(6522.12344));
MyYearSumReportDTO mySumReportDTO1 = new MyYearSumReportDTO();
mySumReportDTO1.setPayTotalAmount(new BigDecimal(2390000));
mySumReportDTO1.setJcAmountPercentage(BigDecimal.valueOf(0.885));
mySumReportDTO1.setJcCountPermillage(BigDecimal.valueOf(0.2394));
mySumReportDTO1.setLength(BigDecimal.valueOf(1700.64003));
mySumReportDTO1.setWidth(BigDecimal.valueOf(7522.12344));

List<MyYearSumReportDTO> list = new ArrayList<>();
list.add(mySumReportDTO);
list.add(mySumReportDTO1);
return list;
}

效果也是很OK:

抛砖引玉,传递‘玩’代码思想,学编程,哎我就是玩。

文章目录
  1. 1. 前言
  2. 2. 正文
    1. 2.1. ① 初步的封装,通过map去标记需要转换的 类属性字段
    2. 2.2. ② 进一步的封装, 配合老朋友自定义注解搞事情