5.1 EL表达式取值规则与隐式对象


5.1 EL表达式取值规则与隐式对象

本节摘要:EL(表达式语言)用 ${...} 一套语法完成视图层的全部取值:点操作符与方括号操作符沿对象链逐层取属性,四级作用域按窄到宽就近查找,param、header、cookie 等隐式对象直达请求元数据,取不到值时宽容地输出空白而非报错。本章大扫除的第一步就是掌握这套规则——它决定了页面里每一个取值表达式的正确写法与排错思路。

点号取值背后的一套规则

第 4 章的推模式立下契约:控制器把数据放进请求作用域,页面负责取出来画。EL 就是取这一步的标准写法:

<%-- 控制器放:request.setAttribute("order", order) --%> <p>订单号:${order.orderNo}</p> <p>收货人:${order.customer.name}</p>

${order.orderNo} 的求值过程分三步。第一步,在作用域里找名为 order 的对象——没写前缀,就按 page、request、session、application 的顺序逐层搜;第二步,对找到的对象解析属性链,orderNo 映射到取值方法,customer.name 则连取两层;第三步,把结果转成字符串写进输出流。方括号操作符是点操作符的通用形态:${order["order-no"]} 能处理带连字符的名字,${bookList[0]} 能按索引取列表元素,${param["user-id"]} 能取请求参数——凡名字不合标识符规范或需要动态拼接的场合,都用方括号。

整条解析管线画出来是这样的:

图5-1 EL 表达式的取值解析管线

图5-1 EL 表达式的取值解析管线

EL 自己的一套隐式对象

注意措辞:EL 的隐式对象与第 2 章那九个不是同一套。第 2 章的是 Java 对象,拿来调方法;EL 的隐式对象本质是几张现成的查找表,拿来取值。最常用的一批见表:

隐式对象 取什么 写法示例
param 请求参数单值 ${param.keyword}
paramValues 请求参数多值 ${paramValues.tag[0]}
header 请求头 ${header["Accept-Language"]}
cookie Cookie 值 ${cookie.theme.value}
pageScope 等四个 指定作用域的属性 ${requestScope.order}
initParam 应用级初始化参数 ${initParam.uploadLimit}

这套表的用法里最有价值的是显式作用域前缀${order} 依赖就近查找,读代码的人不知道数据来自哪一层;${requestScope.order} 则一句话说清出处。青梧书肆的规范因此定了一条:控制器递来的数据一律带 requestScope 前缀写——这是把 4.1 节的"托盘契约"在页面上落成可见的痕迹,也让重名遮挡问题从根源上消失。

运算符方面,EL 提供算术、比较、逻辑三族,外加一个视图层最常用的 empty:${empty keyword} 对 null、空字符串、空集合一律返回真。配合三目写法,页面里最常见的那类"有值才显示"就能写干净:

<c:if test="${not empty order.coupon}"> <p>已优惠:${order.coupon.amount} 元</p> </c:if>

案例:输出空白的三连排查

背景:订单完成页上线后接到反馈:部分顾客的页面"下单成功"后面订单号是空白。没有报错、没有异常日志,前端的样式却因此错位。

操作:按管线三步排查。第一步怀疑空值:对照控制器代码,正常路径确实会放 order 对象——但异常恢复路径(支付超时后重新进入)只放了订单号字符串,没放 order 对象,EL 取 ${order.orderNo} 整链为空,安静输出空白。第二步核对作用域:数据确实在 request 作用域,没有放错层。第三步查重名:会话里存在一个旧版 order 属性(历史代码遗留),不带前缀的写法在某些入口会被它遮住。

结果:控制器补齐异常路径的数据装配;页面表达式统一改成 ${requestScope.order.orderNo},并在服务层保证对象必非空后,用 ${not empty ...} 做展示级判断。

解读:这个案例把宽容失败的两面性演全了:好处是页面没有因一个缺失对象整体 500;坏处是缺陷以空白形态带病上线,靠用户反馈才暴露。对策不是回到抛异常的老路,而是让空值在服务层被拦截、在视图层被显式判断——空值可以容忍,但必须是被看见的容忍。

变式:如果整页 EL 全部原样显示成 ${...} 字符串,那就不是取值问题,而是 1.2 节讲过的旧规范版本或忽略开关把 EL 当成了纯文本——查工程声明的规范版本与部署描述里的开关即可。两种"不取值"病因不同,处理方向完全不同。

💡 关键直觉:EL 是一门"只许提问、不许动手"的语言——只能取值、判断,不能改状态、调任意方法。这个限制当年是为了防止业务逻辑回流向页面;今天读老代码时,它同样是把尺子:页面上用 EL 拼业务规则,一定是把逻辑放错了层。

函数扩展:EL 表达式的合法外挂

EL 自己不调方法,但它留了一个官方外挂口:函数扩展。把工具类里的公开静态方法在标签库描述文件里登记一个名字,页面就能以"前缀加冒号加函数名"的形态调用它——第 5.2 节用到的字符串取长、截断函数,走的正是这套机制。对维护期的价值在两个场景:一是格式化需求(工单号分段显示、金额单位换算)标准函数覆盖不了时,铸一个纯函数给全站复用,比在视图里堆脚本体面得多;二是把散落的日期转换小逻辑收拢进一个工具类,注册后全站统一调用。边界也要划清:函数必须是无副作用的纯函数,入参出参来去干净——它依然活在"只许提问不许动手"的宪法之下,想在函数里偷偷改状态,路是没有的。

排查取值问题的完整走查单

把本节的排查经验固化成走查单,下次遇到空白输出直接照单走查。第一格:表达式带作用域前缀吗?带,去核对数据是否真放在那个作用域;不带,先补上前缀排除重名遮挡。第二格:属性链的每一段都非空吗?从根对象开始逐段核对——控制器装配代码里找到放值的语句,确认对象图每一层都有值。第三格:名字完全一致吗?托盘清单上的属性名与页面表达式逐字符对,大小写也不放过。第四格:拼错的是表达式还是数据?临时把表达式换成常量输出,区分"取值语法问题"还是"数据装配问题"。第五格:整个站点都失灵还是个别页面?个别页面走上面四格,全站失灵直接查 1.2 节的规范版本开关。走查单的价值在于顺序:从最常见的原因查起,多数问题在第二格就现形,不会在错误的方向上空耗。

本节要点回顾

  • 取值管线四步:作用域查找、属性链解析、类型转换、输出,任何空白输出都能沿管线定位;
  • 两操作符互补:点号走常规属性,方括号处理特殊名字、索引与动态键;
  • EL 隐式对象是查找表:param、header、cookie 直达请求元数据,与第 2 章的九大对象是两套体系;
  • 前缀即文档:requestScope 前缀让数据出处一目了然,顺手消灭重名遮挡;
  • 宽容失败要驾驭不要放任:空值拦截在服务层,展示判断用 empty,别让缺陷藏进空白。

取值干净了,页面里还剩判断与循环两类脚本。下一站请出 JSTL 标准标签,把大扫除进行到底。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U