今天聊一个老生常谈的知识点,叫做反射
反射想必大家都熟悉,就是通过传入的类实现一个调动传入的类的方法,主要的作用就是实现一个写一个普遍的方法只需要穿进去不同的类就可以实现调动不同的方法的功能。
首先先给定需求
需求
我们之前做过的功能统计交通量,在交通量的表上添加两个字段,实现两个计算功能,在这里的主要是我们如何对于旧数据进行处理,这是我们一个关键,因为我们的数据都是在服务器上面的,都是长期积攒出来的数据,我们需要对于这些数据继续一个更新操作
思路
我们要对于三个表进行一个更新操作,虽然数据的计算方式是一样的,但是有一张表的字段名和其他两张表不相同,我们想只是一套操作来完成操作的想法无法使用,可以将不同的那个表的参数名穿进来让我们操作。
实现
首先是功能表的功能
@Transactional(rollbackFor = Exception.class)
@Override
public void calcAverageMonthlyTraffic(String trafficId) {
// 获取 全部 交通量指标选择表 的数据
final List<TrafficParamChooseDTO> paramChooseList = this.trafficParamChooseDao.queryAll();
// 获取 交通量等级计算模型
final List<TrafficGradeComputationalEntity> computationalEntities = trafficGradeComputationalDao.selectList(null);
// 获取月平均交通量
final List<MonthTrafficVolumeEntity> trafficVolumeEntities = Lists.newArrayList();
if (StringUtils.isNotBlank(trafficId)) {
MonthTrafficVolumeEntity volumeEntity = this.selectById(trafficId);
if (Objects.nonNull(volumeEntity)) {
trafficVolumeEntities.add(volumeEntity);
}
} else {
List<MonthTrafficVolumeEntity> entities = this.selectList(null);
trafficVolumeEntities.addAll(entities);
}
// 获取机动车类型及折算系数
final List<BaseVehicleEntity> vehicleEntities = this.baseVehicleDao.selectList(null);
final Class<MonthTrafficVolumeEntity> clazz = MonthTrafficVolumeEntity.class;
final List<MonthTrafficVolumeEntity> trafficVolumeEntities2 = TrafficParamChooseUtils.calcAgeLimitCrossSectionTraffic(clazz,
trafficVolumeEntities,
vehicleEntities,
computationalEntities,
paramChooseList,
null);
this.updateBatchById(trafficVolumeEntities2);
}
- 首先在开始获得我们两个需要进行操作,通过交通量指标选择表判断有哪些数据将要参与计算,通过 交通量等级来对于我们数据进行判断。
- 然后就是一个通用的方法,如果不传入id,就是对于所有的老旧数据进行操作,如果是传入id就通过id进行查询,将查询到的对象放在集合中。
- 然后是获得动车类型及折算系数用于和交通量指标选择表进行一起使用计算功能。
- 然后是将我们所在的类转成一个Class 的对象传入我们写好方法里面。
- 将返回的对象进行更新操作。
@Override
public void calcAverageMonthlyTraffic(String trafficId) {
// 获取 全部 交通量指标选择表 的数据
final List<TrafficParamChooseDTO> paramChooseList = this.trafficParamChooseDao.queryAll();
// 获取 交通量等级计算模型
final List<TrafficGradeComputationalEntity> computationalEntities = trafficGradeComputationalDao.selectList(null);
// 获取月平均交通量
final List<StationTrafficEntity> trafficVolumeEntities = Lists.newArrayList();
if (StringUtils.isNotBlank(trafficId)) {
StationTrafficEntity stationTrafficEntity = this.selectById(trafficId);
if (Objects.nonNull(stationTrafficEntity)) {
trafficVolumeEntities.add(stationTrafficEntity);
}
} else {
List<StationTrafficEntity> stationTrafficEntities = this.selectList(null);
trafficVolumeEntities.addAll(stationTrafficEntities);
}
// 获取机动车类型及折算系数
final List<BaseVehicleEntity> vehicleEntities = this.baseVehicleDao.selectList(null);
final Class<StationTrafficEntity> clazz = StationTrafficEntity.class;
Map<Long, String> dictTrafficParam = Maps.newHashMap();
dictTrafficParam.put(Long.valueOf(Constant.SMALL_MEDIUM_BUS), "smallAndMediumBus");
dictTrafficParam.put(Long.valueOf(Constant.SMALL_VAN), "smallVan");
dictTrafficParam.put(Long.valueOf(Constant.BIG_BUS), "bigBus");
dictTrafficParam.put(Long.valueOf(Constant.MEDIUM_VAN), "mediumVan");
dictTrafficParam.put(Long.valueOf(Constant.BIG_VAN), "bigVan");
dictTrafficParam.put(Long.valueOf(Constant.MOST_VAN), "mostVan");
dictTrafficParam.put(Long.valueOf(Constant.CONTAINER_TRUCK), "containerTruck");
final List<StationTrafficEntity> trafficVolumeEntities2 = TrafficParamChooseUtils.calcAgeLimitCrossSectionTraffic(clazz,
trafficVolumeEntities,
vehicleEntities,
computationalEntities,
paramChooseList,
dictTrafficParam);
this.updateBatchById(trafficVolumeEntities2);
}
这是不同于另外的两个表的一个表,不同的是因为我们有两个表是相同的所以我们在要处理的函数内部直接规定了各个值的传递的方式,但是不同的表就会出现相同的变量但是明明不相同,所以我们要将不同的数据提前处理好,这样可以通过参数的方式判断是否是特殊的那张表。
/**
* 计算获取【设计使用年限内断面累计大型客车和货车交通量额】和【设计交通荷载等级】
*
* @param clazz 交通量表数据实体类型
* @param trafficVolumeEntities 交通量表数据列表
* @param vehicleEntities 机动车类型及折算系数数据列表
* @param computationalEntities 交通量等级计算模型数据列表
* @param paramChooseList 交通量指标选择表 数据实体
* @param dictTrafficParam 字典和车型对照关系
* @param <E> 交通量表数据实体类型泛型
* @return List 交通量表数据列表
*/
public static <E> List<E> calcAgeLimitCrossSectionTraffic(final Class<E> clazz,
final List<E> trafficVolumeEntities,
final List<BaseVehicleEntity> vehicleEntities,
final List<TrafficGradeComputationalEntity> computationalEntities,
final List<TrafficParamChooseDTO> paramChooseList,
Map<Long, String> dictTrafficParam) {
if (CollectionUtils.isEmpty(paramChooseList) || CollectionUtils.isEmpty(computationalEntities)) {
throw new RRException("无法获取 交通量指标选择表 的数据 或 无法获取 交通量指标选择表 的数据");
}
if (Objects.isNull(dictTrafficParam)) {
dictTrafficParam = Maps.newHashMap();
dictTrafficParam.put(Long.valueOf(Constant.SMALL_MEDIUM_BUS), "smallAndMediumPassengerFlow");
dictTrafficParam.put(Long.valueOf(Constant.SMALL_VAN), "pickupTruckFlow");
dictTrafficParam.put(Long.valueOf(Constant.BIG_BUS), "busFlow");
dictTrafficParam.put(Long.valueOf(Constant.MEDIUM_VAN), "mediumTruckFlow");
dictTrafficParam.put(Long.valueOf(Constant.BIG_VAN), "largeTruckFlow");
dictTrafficParam.put(Long.valueOf(Constant.MOST_VAN), "largeCargoFlow");
dictTrafficParam.put(Long.valueOf(Constant.CONTAINER_TRUCK), "containerFlow");
}
TrafficGradeComputationalEntity computationalEntity = computationalEntities.get(0);
Long computationalparam1 = computationalEntity.getParam1();
Long computationalparam2 = computationalEntity.getParam2();
Long computationalparam3 = computationalEntity.getParam3();
Long computationalparam4 = computationalEntity.getParam4();
if (Objects.isNull(computationalparam1) || Objects.isNull(computationalparam2) ||
Objects.isNull(computationalparam3) || Objects.isNull(computationalparam4)) {
throw new RRException("无法获取 有效的 交通量指标选择表 的数据");
}
BigDecimal computationalparamDecimal1 = BigDecimal.valueOf(computationalparam1);
BigDecimal computationalparamDecimal2 = BigDecimal.valueOf(computationalparam2);
BigDecimal computationalparamDecimal3 = BigDecimal.valueOf(computationalparam3);
BigDecimal computationalparamDecimal4 = BigDecimal.valueOf(computationalparam4);
// 转换为车型 -> 系数
final Map<Long, BigDecimal> coefficientMp = Maps.newHashMap();
for (BaseVehicleEntity vehicleEntity : vehicleEntities) {
coefficientMp.put(Long.valueOf(vehicleEntity.getTwoLevelType()), vehicleEntity.getCoefficient());
}
Method setAgeLimitCrossSectionTraffic = BeanUtils.findDeclaredMethod(clazz, "setAgeLimitCrossSectionTraffic", BigDecimal.class);
Method setTrafficLoadLevel = BeanUtils.findDeclaredMethod(clazz, "setTrafficLoadLevel", String.class);
if (Objects.isNull(setAgeLimitCrossSectionTraffic)) {
throw new RRException("计算数据赋值错误,无法获取setAgeLimitCrossSectionTraffic对象");
}
if (Objects.isNull(setTrafficLoadLevel)) {
throw new RRException("计算数据赋值错误,无法获取setTrafficLoadLevel对象");
}
// 开始计算
for (E trafficVolumeEntity : trafficVolumeEntities) {
BigDecimal ageLimitCrossSectionTraffic = BigDecimal.ZERO;
for (TrafficParamChooseDTO trafficParamChoose : paramChooseList) {
if (!trafficParamChoose.startUsing()) {
continue;
}
Long param = trafficParamChoose.getParam();
String dictTrafficParamStr = dictTrafficParam.get(param);
BigDecimal coefficient = coefficientMp.get(param);
if (StringUtils.isBlank(dictTrafficParamStr) || Objects.isNull(coefficient)) {
continue;
}
Method declaredMethod = BeanUtils.findDeclaredMethod(clazz,
"get" + dictTrafficParamStr.substring(0, 1).toUpperCase() + dictTrafficParamStr.substring(1));
if (Objects.isNull(declaredMethod)) {
log.warn("无法获取【{}】的getter方法", dictTrafficParamStr);
continue;
}
try {
Object trafficParamNum = declaredMethod.invoke(trafficVolumeEntity);
BigDecimal trafficParamDecimal = null;
if (trafficParamNum instanceof Long) {
trafficParamDecimal = BigDecimal.valueOf((Long) trafficParamNum);
} else if (trafficParamNum instanceof Integer) {
trafficParamDecimal = BigDecimal.valueOf((Integer) trafficParamNum);
} else if (trafficParamNum instanceof Double) {
trafficParamDecimal = BigDecimal.valueOf((Double) trafficParamNum);
} else {
trafficParamDecimal = BigDecimal.valueOf(Double.parseDouble(String.valueOf(trafficParamNum)));
}
ageLimitCrossSectionTraffic = trafficParamDecimal.multiply(coefficient).add(ageLimitCrossSectionTraffic);
} catch (Exception e) {
log.warn("无法获取【" + dictTrafficParamStr + "】的值", e);
}
}
try {
setAgeLimitCrossSectionTraffic.invoke(trafficVolumeEntity, ageLimitCrossSectionTraffic);
String trafficLoadLevel;
if (ageLimitCrossSectionTraffic.compareTo(computationalparamDecimal1) < 0) {
trafficLoadLevel = "轻";
} else if (ageLimitCrossSectionTraffic.compareTo(computationalparamDecimal2) < 0) {
trafficLoadLevel = "中";
} else if (ageLimitCrossSectionTraffic.compareTo(computationalparamDecimal3) < 0) {
trafficLoadLevel = "重";
} else if (ageLimitCrossSectionTraffic.compareTo(computationalparamDecimal4) < 0) {
trafficLoadLevel = "特重";
} else {
trafficLoadLevel = "极重";
}
setTrafficLoadLevel.invoke(trafficVolumeEntity, trafficLoadLevel);
} catch (Exception e) {
log.warn("计算数据赋值错误,{}", trafficVolumeEntity);
log.warn("计算数据赋值错误", e);
}
}
return trafficVolumeEntities;
}
具体的输入输出的方法参数和返回值都在注解上面写的清清楚楚我就不再复述一遍了,我来大概的解释一下代码吧
- 首先得第一个if是用来将判断我们的交通量指标选择表计算模型是否能够正常使用。
- 第二个if是判断是否存在一个map中存放着字段名对应的变量名。
- 后面判断我们交通量指标选择表模型能够正常使用。
- 然后是通过工具类生成一个hashmap,将对应的车型和系数放进去便于我们进行运算。
- 后面是通过反射的方式将entity中的set方法拿出来用于给我们指定的变量赋值,
- 然后判断是否为空两个方法
- 开始计算,首先要根据传入的类型的数量进行遍历,然后是判断
交通量指标选择表
中哪一些字段是需要进行计算的, - 拿到我们要操作的变量名,和变量名相关的系数
- 然后具体通过反射的方式将我们的数据也通过entity中的get方法拿出来
- 然后将不同的类型转换成bigdecimal的形式
- 最后使用bigdecimal的方式将其计算出来
- 然后将我们计算出来的值通过set的方式直接set进去
- 然后通过和我们制定好的参数记性比较,得到一个结果通过反射放进数据里面
这就是这个方法的全部,这是我第一次感觉到了反射的用处,也感觉到了反射的区别,也感觉到了自己只是的薄弱,希望自己以后能够将自己会的东西变成自己能够用的东西。