利用反射实现“风控“功能
前言
在我们的业务系统中经常会有这样的一种需求,就是需要根据某些规则,对一些实体对象进行过滤,有可能在多个地方都会用到,也不可能在每个地方根据需求来编写重复的内容,所以就有这次根据配置的内容,并且采用Java的反射技术来实现类似“风控”的卡控功能。
反射原理图(来源百度百科):
一、实现思路
1、首先是一张规则定义表 如图(仅做示例使用)
2、业务和规则的关联关系 如图(仅做示例使用)
3、在前端可以实现,对新增的规则进行管理,对某一个具体的业务对象和规则进行关联 如图(仅做示例使用)
二、应用场景
1、可以在流程申报的时候进行风控
2、需要对数据进行同一个【业务规则】进行过滤
…
三、大体代码
1、风控核心方法
/**
* @param bizName 业务表名
* @param bizId 业务表id
* @param riskCheckList 统一封装的待风控对象(可自己根据需要修改对象)
* @param consumer 函数式回调 可自己根据需要调用
* @return
*/
public ResultMessage checkRuleBatch(String bizName, String bizId, List<RiskCheck> riskCheckList, Consumer<RiskCheck> consumer) {
// 根据bizName和bizId查询出所有的启用规则
// 循环查询到的规则
rules.forEach(rule -> {
// 去取方法名称类名以及方法名称
String ruleMethod = MapUtil.getStr(rule, "rule_method");
String[] tmp = ruleMethod.split("[.]");
if (tmp.length != 2) {
return;
}
String beanId = tmp[0];
String method = tmp[1];
// 从容器中取得对应的规则bean
Object serviceBean = AppUtil.getBean(beanId);
if (serviceBean != null) {
Method invokeMethod;
try {
// 调用反射取得执行方法
invokeMethod = serviceBean.getClass().getDeclaredMethod(method, new Class[]{Map.class});
rule.put("invokeMethod", invokeMethod);
rule.put("serviceBean", serviceBean);
} catch (Exception e) {
e.printStackTrace();
}
}
});
// 循环待检查对象 一个对象可能会对应多个规则
waitCheckList.forEach(riskObjDto -> {
// 根据自己的业务需求 处理自己的参数
rules.forEach(rule -> {
String ruleName = MapUtil.getStr(rule, "rule_name");
//场景编码
Object serviceBean = rule.get("serviceBean");
Method invokeMethod = (Method) rule.get("invokeMethod");
if (serviceBean != null && invokeMethod != null) {
try {
// 统一参数定义 枚举类
Map<Common.ParamKey, Object> param = new HashMap<BpmDefinitionRuleManager.ParamKey, Object>();
param.put(Common.ParamKey.xxx, 风控参数1);
param.put(Common.ParamKey.xxx, 风控参数2);
// 调用反射进入规则对应的规则方法 ResultMessage统一结果返回
ResultMessage result =(ResultMessage)invokeMethod.invoke(serviceBean, param);
if (ResultMessage.SUCCESS != result.getResult()) {
// 成功处理
}eles{
// 失败处理
}
// 记录日志
} catch (Exception e) {
e.printStackTrace();
}
}
});
consumer.accept(riskObjDto);
}
2、规则对应方法(仅示例使用) 需要根据自己的黑名单业务进行修改
/**
* 黑名单配置拦截
*
* @param map 统一参数
* @return
* @throws CheckedException
*/
public ResultMessage checkBlacklist(Map<ParamKey, Object> map) throws CheckedException {
// 取得参数
String xxx = MapUtil.getStr(map, ParamKey.xxx);
// 调用黑名单的业务方法
int count= checkBlackService.checkBlacklist(xxx);
if (count> 0) {
return ResultMessage.getFail("您被记录为黑名单,无法操作!");
} else {
return ResultMessage.getSuccess("检查通过!");
}
}
3、具体的黑名单检查方法就根据自己系统实现
四、功能优化
1、Java的反射是比较慢的,并且还是对象列表循环,规则循环,两层循环,所以必须要优化执行效率。
优化思路:
采用异步多线程的方法(已实现)
不贴代码了,说一下思路,将待检查的对象列表分组,然后循环调用异步风控核心方法(使用@Async注解要注意,不要写在同一个类,因为它是通过代理实现的)
2、记录风控日志
记录日志 方便后续排查,或者用于数据全景分析(已实现)
3、业务数据的及时更新
在对业务数据有实时性的要求是,可以通过函数式的accpet实现数据的及时更新(已实现)
总结
算是反射的一种用法吧,如有错误,请指正。