|
简介SpEL常见用法快速入门实际应用SpEL内部实现的简单梳理总结简介SpringExpressionLanguage(简称SpEL,Sp:Spring,EL:ExpressionLanguage)是一个支持运行时查询和操作对象图的强大的表达式语言。在Spring产品组合中与我们常见的Beans模块、Core核心模块、Context上下文模块一起组成了Spring的核心容器,是表达式计算的基础,支持在运行时查询和操作对象,可以与基于XML和基于注解的Spring配置还有bean定义一起使用。Spring的体系结构SpEL常见用法SpEL的语法类似于JSP中EL表达式,使用#{…}作为定界符,所有在大框号中的字符都将被认为是SpEL。SpEL支持如下表达式:SpEL字面量:整数:#{8}小数:#{8.8}科学计数法:#{1e4}String:#{'string'}Boolean:#{true}SpEL引用bean,属性和方法:引用其他对象:#{car}引用其他对象的属性:#{car.brand}调用其它方法,还可以链式操作:#{car.toString()}调用静态方法静态属性:#{T(java.lang.Math).PI}SpEL支持的运算符号:算术运算符:+,-,*,/,%,^(加号还可以用作字符串连接)比较运算符:,==,>=, monitorConfigMap = apolloConfigService.getMonitorConfigMap(); if (MapUtils.isEmpty(monitorConfigMap) || !monitorConfigMap.containsKey(code)) { return; } MonitorConfig monitorConfig = monitorConfigMap.get(code); //当有返回值时需要校验一下结果是否符合预期 String resultType = monitorConfig.getResultType(); if (checkResult(result, resultType)) { return; } //获取入参的SpEL表达式进行解析 String monitorSpEL = monitor.monitorSpEL(); String monitorTrace = String uuid = StringUtils.isNotBlank(monitorSpEL) ? SpelParseUtil.generateKeyBySpEL(monitorSpEL, joinPoint) : StringUtils.EMPTY_STRING; //截取一下异常信息 String otherParamsJson = ""; if (Objects.nonNull(ex)) { Map otherParams = Maps.newHashMap(); String stackTraceAsString = Throwables.getStackTraceAsString(ex); String errMsg = stackTraceAsString; if (stackTraceAsString.length() > NumberConstant.NUMBER_512) { errMsg = stackTraceAsString.substring(0,NumberConstant.NUMBER_512) + "..."; } otherParams.put("异常信息", errMsg); otherParamsJson = JsonUtil.silentObject2String(otherParams); } // 异步发送报警信息 asyncSendMonitor(scenes, monitorTrace, otherParamsJson); }}......private static SpelExpressionParser parser = new SpelExpressionParser();private static DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();public static String generateKeyBySpEL(String spelString, JoinPoint joinPoint) { // 通过joinPoint获取被注解方法 MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); // 使用spring的DefaultParameterNameDiscoverer获取方法形参名数组 String[] paramNames = nameDiscoverer.getParameterNames(method); // 解析过后的Spring表达式对象 Expression expression = parser.parseExpression(spelString); // spring的表达式上下文对象 EvaluationContext context = new StandardEvaluationContext(); // 通过joinPoint获取被注解方法的形参 Object[] args = joinPoint.getArgs(); // 给上下文赋值 for (int i = 0; i bizOrderContext = BizOrderContext.create(OrderEventEnum.C1_CHANGE_JM_PRICE, request); ZzAssert.isTrue(stateMachine.isCanFire(bizOrderContext), BizErrorCode.ORDER_STATUS_CHANGED); stateMachine.fire(bizOrderContext); return bizOrderContext.getResponse();}示例效果:SpEL内部实现的简单梳理SpEL在Spring内部的实现可以简单理解如下:语法分析首先用户调用ExpressionParser#parseExpression方法触发表达式解析。表达式解析器在内部先进行词法解析,将字符串形式的表达式拆分成不同的Token,如1+2表达式会被拆分成1、+、2三部分。解析时同时会参考上下文ParserContext,如上述示例中的#{name}表达式,解析器会先去掉前后缀#{},然后再进行解析。随后Token将被转换为抽象语法树,在内部使用SpelNode表示,为了简化用户操作语法树被包装到Expression。用户使用Expression#getValue方法获取表达式的值,在内部也会参考评估上下文EvaluationContext进行解析。总结本文只是简单的介绍了如何使用,实际场景中SpEL随处可见,除了上文中示例的监控报警之外,动态创建Documet类的Index、动态加解锁、接口缓存等都有非常多的实践。除此之外spring应用中常见的@cacheable、@Value,以及SpringSecurity框架中的@PreAuthorize、@PostAuthorize、@PreFilter、@PostFilter中都有SpEL的身影。参考资料[1]SpringExpressionLanguage(SpEL):https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions[2]SpEL你感兴趣的实现原理浅析:https://cloud.tencent.com/developer/article/1497676关于作者钱曙光,转转C2B业务研发工程师。
|
|