SpEL
SpEL
即Spring
表达式语言,可以在运行时评估表达式并生成值。
应用场景
动态参数配置:
可以通过
SpEL
将应用程序中的各种参数配置化。
- 例如配置文件中的数据库连接信息、业务规则等。
通过动态配置,可以在运行时根据不同的环境或需求来进行灵活的参数设置。
运行时注入:
使用
SpEL
,可以在运行时动态注入属性值,而不需要在编码时硬编码。条件判断与业务逻辑:
SpEL
支持复杂的条件判断和逻辑计算,可以方便地在运行时根据条件来执行特定的代码逻辑。表达式模板化:
SpEL
支持在表达式中使用模板语法,允许将一些常用的表达式作为模板。
- 然后在运行时通过填充不同的值来生成最终的表达式。
系统场景
Excel 解析:
SpEL
可以用于解析Excel
表格中的数据。可以使用
SpEL
表达式来指定需要解析的单元格、行、列等等,提取数据并应用相应的逻辑。
- 这使得解析过程更加灵活和可扩展。
规则引擎:
在使用规则引擎时,
SpEL
可以用于定义规则条件和执行动作。通过
SpEL
表达式,可以动态地根据特定的条件对数据进行处理和决策。模板引擎:
SpEL
可以用于填充模板数据。通过
SpEL
表达式,可以在模板中引用对象的属性、方法或函数。配置文件解析:
SpEL
可以用于解析配置文件中的动态值。通过
SpEL
表达式,可以在配置文件中引用其他属性或方法的值。验证规则:
在数据验证的场景中,
SpEL
可以用于定义验证规则。通过
SpEL
表达式,可以对数据进行复杂的验证和处理。
简单举例
/**
* 验证数字是否大于10
*/
public String spELSample(int number) {
// 创建ExpressionParser对象,用于解析SpEL表达式
ExpressionParser parser = new SpelExpressionParser();
String expressionStr = "#number > 10 ? 'true' : 'false'";
Expression expression = parser.parseExpression(expressionStr);
// 创建EvaluationContext对象,用于设置参数值
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("number", number);
// 求解表达式,获取结果
return expression.getValue(context, String.class);
}
实现动态参数处理策略
举例:每新增一个渠道接入时不需要进行代码开发,只需在配置表中维护关联关系。
根据标识匹配对应策略标识,根据策略标识找到具体参数处理策略表达式。
// 定义解析工具类
public class ExpressionUtil {
private final ExpressionParser expressionParser = new SpelExpressionParser();
// 创建上下文对象,设置自定义变量、自定义函数
public StandardEvaluationContext createContext(String instAccountNo){
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("instAccountNo", instAccountNo);
// 注册自定义函数
this.registryFunction(context);
return context;
}
// 注册自定义函数
private void registryFunction(StandardEvaluationContext context) {
try {
context.addPropertyAccessor(new MapAccessor());
context.registerFunction("yuanToCent", ExpressionHelper.class.getDeclaredMethod("yuanToCent", String.class));
context.registerFunction("substringBefore", StringUtils.class.getDeclaredMethod("substringBefore",String.class,String.class));
} catch (Exception e) {
log.info("SpEL函数注册失败:", e);
}
}
// 开启缓存,使用解析器解析表达式,返回表达式对象
@Cacheable(key="'getExpressionWithCache:'+#cacheKey", unless = "#result == null")
public Expression getExpressionWithCache(String cacheKey, String expressionString) {
try {
return expressionParser.parseExpression(expressionString);
} catch (Exception e) {
log.error("SpEL表达式解析异常,表达式:[{}]", expressionString, e);
throw new BizException(ReturnCode.EXCEPTION.getCode(),String.format("SpEL表达式解析异常:[%s]",expressionString),e);
}
}
}
// 定义解析类
public class ExpressionService {
@Resource
private ExpressionUtil expressionUtil;
public FileBillReqDTO transform(ChannelEntity channel, String instAccountNo) throws Exception {
// 获取上下文对象(变量设置、函数设置)
StandardEvaluationContext context = expressionUtil.createContext(instAccountNo);
// 获取支付请求类对象
FileBillReqDTO target = ClassHelper.newInstance(FileBillReqDTO.class);
// t_channel_api表配置的api映射表达式
for (ChannelApiEntity api : channel.getApis()) {
// 通过反射获取FileBillReqDTO类属性名对象
Field field = ReflectionUtils.findField(FileBillReqDTO.class, api.getFieldCode());
// 表达式
String expressionString = api.getFieldExpression();
// 开启缓存,使用解析器解析表达式,返回表达式对象
Expression expression = expressionUtil.getExpressionWithCache(api.fieldExpressionKey(), expressionString);
// 通过表达式对象获取解析后的结果值
Object value = expression.getValue(context, FileBillReqDTO.class);
// 将结果通过反射赋值给FileBillReqDTO对象中指定属性字段
field.setAccessible(true);
field.set(target, value);
}
// 返回解析赋值后的完整对象
return target;
}
}
// 调用类
public class ChannelApplyFileClient {
@Resource
private CNRegionDataFetcher cnRegionDataFetcher;
@Resource
private ExpressionService expressionService;
@Resource
private ChannelRepository channelRepository;
public String applyFileBill(String instCode, String instAccountNo) {
// 根据渠道码查询t_channel、t_channel_api表,返回ChannelEntity对象
ChannelEntity channel = channelRepository.findByInstCode(instCode);
// 通过SpEL解析t_channel_api表中表达式,并将值赋值给对应属性中,返回完整请求对象
FileBillReqDTO channelReq = expressionService.transform(channel, instAccountNo);
// 请求支付系统拉取账单文件,同步返回处理中,异步MQ通知下载结果
BaseResult<FileBillResDTO> result = cnRegionDataFetcher.applyFileBill(channelReq, "资金账单下载");
return "处理中";
}
}
Excel解析
举例:从不同的渠道下载账单,并对账单进行解析,解析后的数据落入流水表。
注意不同渠道的账单的头字段和格式存在差异。
传统的方式中,解析
Excel
通常需要通过创建实体类来映射Excel
的结构和数据。每个实体类代表一个
Excel
行或列,需要手动编写代码来将Excel
数据解析为相应的实体对象。以下是使用
SpEL
方式动态解析Excel
的一般步骤:
- 使用
Apache POI
等工具读取Excel
数据表。- 根据配置表,将
Excel
中的列与SpEL
表达式进行关联。- 使用
SpEL
解析器,在运行时解析这些SpEL
表达式。- 将解析后的结果做数据清洗后落表,应用于现金流打标业务。