XxlJobAnalyticParamUtil(乐乐独记)
1、业务背景
这篇博客主要是我们公司在用针一个轻量级分布式任务调度平台,名叫xxlJob,也可以简单理解为一种定时任务,在这个平台中,可以传递一个字符串参数给具体的业务代码,由于传递参数的方式大家各不相同,而且太乱,所以我就定义了这一套规范,类似于web项目中get方式传参的模式,传一个字符串给后端,后端定义好一个模型,用我下面的工具类可以直接转换成相应的模型并赋值。使得参数更加清晰明了,代码如下:
2、代码
2.1、引用pom文件
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
2.2、工具类
package xxlJob;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* xxlJob 解析参数类
* 目前支持的参数形式如下所示,以 & 分隔参数个数:
* stringType=ale
* &integerType=1000
* &stringListType=233021258120003999,333020570110869001,141092358120000001
* &bigDecimalType=500.6
* &mapType={a:111,b:222}
*目前支持的数据类型:
* String dealNum;
* Integer pageSize;
* List<String> shopIdList;
* BigDecimal amount;
* Map<String, String> stringMap;
* xxlJob按照上面字符串传入,模型按照后面写就可以映射的上,主要是参数名得对应上
* @author k10156 杜佳乐
* @date 20220803
*/
@Slf4j
public class XxlJobAnalyticParamUtil {
/**
* 字段分隔符
*/
private static final String FIELDS_SPLIT = "&";
/**
* 字段的名与value分隔符
*/
private static final String FIELD_NAME_AND_VALUE_SPLIT = "=";
/**
* List 元素之间的分隔符
*/
private static final String LIST_SPLIT = ",";
/**
* 支持映射的字段数据类型
*/
private static final Class[] FIELD_TYPE_ARRAY = {
String.class,
Integer.class,
List.class,
BigDecimal.class,
Map.class
};
/**
* 外部提供的方法
*
* @param taskName 任务名称
* @param param 传入的参数字符串
* @param resultObject 将传入的参数字符串整合成模型返回
* @return 返回整合的模型
*/
public static Object execute(String taskName, String param, Object resultObject) {
log.info("execute方法开始:");
// 执行真正的方法
return analyticParameter(taskName, param, resultObject);
}
/**
* 解析参数主方法
* @param taskName 任务名称
* @param param 传入的参数字符串
* @param resultObject 将传入的参数字符串整合成模型返回
* @return 返回整合的模型
*/
private static Object analyticParameter(String taskName, String param, Object resultObject) {
log.info("analyticParameter方法开始:");
if (null == param || "".equals(param) || null == resultObject){
return resultObject;
}
// 获取到返回模型的所有字段列表(所有信息)
Field[] fields = resultObject.getClass().getDeclaredFields();
if (fields.length == 0){
return resultObject;
}
List<String> fieldNameList = new ArrayList<String> (fields.length * 2);
// 将所有的字段名称全部放进去
for ( int i = 0; i < fields.length; i++){
fieldNameList.add(fields[i].getName());
}
// 取出传入的字段名称
String[] parameterList = param.split(FIELDS_SPLIT);
for ( int i = 0; i < parameterList.length; i++){
resultObject = matchParameter(taskName, parameterList[i], fieldNameList, resultObject);
}
return resultObject;
}
/**
* 匹配单个参数并赋值给模型
* @param taskName 任务名称
* @param requestParamValue 单个参数的请求值 dealNum:100
* @param fieldNameList 传入模型的字段属性列表
* @param resultObject 需要继续注入属性值的返回结果模型
* @return 将增加了新的参数值注入的结果模型返回
*/
private static Object matchParameter(String taskName, String requestParamValue,
List<String> fieldNameList, Object resultObject)
{
log.info("matchParameter方法开始:");
// 定义一个返回结果模型临时变量,不能对传入的参数直接修改
Object resultObjectTemp = resultObject;
if (null == requestParamValue || "".equals(requestParamValue)){
return resultObjectTemp;
}
// 不等于空时,分隔
String[] singleParamSplit = requestParamValue.split(FIELD_NAME_AND_VALUE_SPLIT);
if (singleParamSplit.length != 2){
log.info("[{}]分隔参数[{}]时出现异常,请检查参数。", taskName, requestParamValue);
return resultObjectTemp;
}// 校验名称是否相等,如果不相等,则返回
String currentParamName = singleParamSplit[0];
if (fieldNameList.contains(currentParamName)) {
String fillContent = singleParamSplit[1];
// 匹配方法并赋值填充内容
matchMethodAndFillContent(resultObjectTemp, currentParamName, fillContent, taskName,
requestParamValue);
}else{
log.info("[{}]赋值参数[{}]时名称不存在,请检查参数。", taskName, requestParamValue);
}
// 返回组装好的模型
return resultObjectTemp;
}
/**
* 匹配方法并赋值填充内容
* @param resultObjectTemp 需要返回映射的对象
* @param currentParamName 当前需要映射的参数名称
* @param fillContent 当前需要映射的参数的填充内容
* @param taskName 当前执行xxlJob任务的名称
* @param requestParamValue 当前需要映射匹配的请求原始内容,如:dealNum:100
*/
private static void matchMethodAndFillContent(Object resultObjectTemp, String currentParamName, String fillContent,
String taskName, String requestParamValue) {
log.info("matchMethodAndFillContent方法开始:");
// set 方法临时变量
Method setMethod = null;
// 是否赋值上
boolean matchSuccessFlag = false;
for ( int index = 0; index < FIELD_TYPE_ARRAY.length ; index++){
try {
setMethod = resultObjectTemp.getClass().getMethod("set" +
// 对首字母大写
currentParamName.substring(0, 1).toUpperCase() + currentParamName.substring(1),
// 方法的参数类型
FIELD_TYPE_ARRAY[index]);
// 若匹配上,则会走下面这行反射执行 set 方法
invokeExecute(setMethod, fillContent, resultObjectTemp, FIELD_TYPE_ARRAY[index]);
matchSuccessFlag = true;
break;
} catch (Exception e){
log.debug("errorStack:", e);
// 进行下一个类型的匹配,这里不需要打印日志
}
}
if (matchSuccessFlag) {
log.info("[{}]赋值参数[{}]时完成。", taskName, requestParamValue);
}else {
log.info("[{}]赋值参数[{}]失败。", taskName, requestParamValue);
}
}
/**
* 根据不同的类型,执行不同的 invoke 设置数据调用方法
* @param setMethod set的方法代理对象
* @param fillContent 需要填充的参数内容
* @param resultObjectTemp 需要返回的对象
* @param clazz 不同的参数类型
* @throws InvocationTargetException 异常1
* @throws IllegalAccessException 异常2
*/
private static void invokeExecute(Method setMethod, String fillContent, Object resultObjectTemp, Class clazz)
throws InvocationTargetException, IllegalAccessException {
log.info("invokeExecute方法开始:");
if (clazz == String.class){
setMethod.invoke(resultObjectTemp, fillContent);
}
else if (clazz == Integer.class) {
setMethod.invoke(resultObjectTemp, Integer.valueOf(fillContent));
}
else if (clazz == List.class){
setMethod.invoke(resultObjectTemp, Arrays.asList(fillContent.split(LIST_SPLIT)));
}
else if (clazz == BigDecimal.class){
setMethod.invoke(resultObjectTemp, new BigDecimal(fillContent));
}
else if (clazz == Map.class){
setMethod.invoke(resultObjectTemp, JSON.parseObject(fillContent, Map.class));
}
else {
log.info("不支持该类型的映射!");
}
}
}
2.3、测试类
测试模型
@Data
public class TestModel {
private String stringType;
private String integerType;
private List<String> stringListType;
private BigDecimal bigDecimalType;
private Map<String, String> mapType;
}
单元测试
String param = "stringType=ale&integerType=1000&stringListType=233021258120003999,333020570110869001,141092358120000001&bigDecimalType=500.6&mapType={a:111,b:222}";
TestModel list = (TestModel) XxlJobAnalyticParamUtil.execute("", param, new TestModel());
System.out.println(list);