使用反射摆脱了过长的if/else校验使用反射摆脱了过长的

使用反射摆脱了过长的if/else校验

「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战

引言

最近做一个业务,需要做额度限制,超出额度做特殊业务处理,封装了一个方法专门做额度限制的校验,由于需要判断的金额字段太多导致if/else判断也随之增加,所以想到使用反射去做处理,减少代码量的同时,后续扩展性也比较强。

业务实体

下面是业务实体类,对字段名称做了特殊处理。

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ExpendLimit{
    private BigDecimal count1Amt;
	
    private BigDecimal count2Amt;
	
    private BigDecimal count3Amt;
	
    private BigDecimal count4Amt;
	
    ....
    // 此处目前大概有12个金额字段需要做校验
}
复制代码

代码对比

封装方法使用两个参数,currentResidue表示当前剩余额度,currentTotal表示本期付款,逻辑为本期付款对象里如果count?Amt大于0,并且大于currentResidue对应字段的金额时,返回false,做超额逻辑处理。

使用反射前

private boolean checkResidue(ExpendLimit currentResidue, ExpendLimit currentTotal){
	boolean result = true;
	if (currentTotal.getCount1Amt().compareTo(BigDecimal.ZERO)>0){
		if (currentTotal.getCount1Amt().compareTo(currentResidue.getCount1Amt())>0){
			result = false;
		}
	} else if (currentTotal.getCount2Amt().compareTo(BigDecimal.ZERO)>0){
		if (currentTotal.getCount2Amt().compareTo(currentResidue.getCount2Amt())>0){
			result = false;
		}
	}...
	// 如果写完全需要40多行,且如果后续业务增加需要继续加代码
	return result;
}
复制代码

使用反射后

private boolean checkResidue(ExpendLimit currentResidue, ExpendLimit currentTotal) {
	boolean result = true;
	Class<? extends ExpendLimit> clazz = currentResidue.getClass();
	Field[] fields = clazz.getDeclaredFields();
	try {
		for (Field field : fields) {
			//打开私有访问
			field.setAccessible(true);
			//获取属性
			String name = field.getName();
			if (name.contains("Amt")) {
				//获取属性值
				BigDecimal totalAmt = (BigDecimal) field.get(currentTotal);
				if (totalAmt.compareTo(BigDecimal.ZERO) > 0) {
					BigDecimal residueAmt = (BigDecimal) field.get(currentResidue);
					if (totalAmt.compareTo(residueAmt) > 0) {
						result = false;
						break;
					}
				}
			}
		}
	} catch (IllegalAccessException e) {
		e.printStackTrace();
	}
	return result;
}
复制代码

反射的基本原理

  • 编译阶段:编译期装载所有的类,将每个类的信息保存至Class类对象中,每一个类对应一个Class对象(不懂为啥每个类只有一个Class类对象的可以自己查询一下双亲委派模型)
  • 获取Class对象:调用x.class/x.getClass()/Class.forName() 获取x的Class对象clz
  • 使用反射进行操作:通过clz对象获取Field/Method/Constructor对象进行进一步操作。

反射使用的API

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类成员变量
  • java.lang.reflect.Constructor:代表类的构造器

反射提供的功能

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时获取泛型的信息
  • 在运行时调用任意一个对象的成员变量和方法
  • 在运行时处理注解
  • 生成动态代理

总结

通过使用反射,改变了过长的if/else判断。在我的业务里其实只使用了java.lang.reflect.Field类获取对象属性做业务判断。其实反射在很多框架中都有使用,Spring的动态代理也是应用了反射,不管是CGLAB动态代理还是JDK动态代理其根本都是使用反射。