2. 立即求值与延迟求值

在JSF中,EL的核心作用是连接视图层(通常为XHTML)和Java后端(如托管Bean或容器管理对象)。本文聚焦EL 2.2版本,该版本包含两种主要语法形式:

2.1. 立即求值语法EL

源自JSP时代的遗留语法,使用 ${...} 格式:

${ELBean.value > 0}

关键特性:

  • 单次求值:仅在页面生命周期开始时求值一次
  • 只读访问:仅能读取Bean属性
  • 命名约定限制:必须严格遵循JavaBean规范

    简单粗暴地说:这种语法灵活性较差,现代JSF开发中已较少使用

2.2. 延迟求值语法EL

JSF原生语法,使用 #{...} 格式:

#{ELBean.value > 0}

核心优势:

  • 生命周期同步:在JSF渲染流程的多个阶段自动求值
  • 读写支持:既能读取也能修改Bean属性
  • 方法调用:支持调用任意方法(EL 2.2+支持传参)

统一EL规范允许两种语法共存,但实际开发中延迟求值才是主流选择。

3. 统一EL核心机制

统一EL提供两种表达式类型:值表达式和方法表达式。示例应用访问地址:

http://localhost:8080/jsf/el_intro.jsf

3.1. 值表达式

根据使用场景实现数据双向绑定:

<!-- 读取Bean属性 -->
Hello, #{ELBean.firstName}

<!-- 设置Bean属性 -->
<h:inputText id="firstName" value="#{ELBean.firstName}" required="true"/>

关键点:变量必须符合JavaBean命名规范,表单提交时自动完成数据绑定

3.2. 方法表达式

调用托管Bean中的公共方法:

<!-- 基础调用 -->
<h:commandButton value="Save" action="#{ELBean.save}"/>

<!-- EL 2.2+ 支持传参 -->
<h:inputText id="firstName" binding="#{firstName}" required="true"/>
<h:commandButton value="Save"
  action="#{ELBean.saveFirstName(firstName.value.toString().concat('(passed)'))}"/>

踩坑提示:参数传递无需特殊符号,直接引用组件属性即可

3.3. 隐式EL对象

JSF预置的容器管理对象: | 对象 | 说明 | |------|------| | #{application} | Web应用实例(同servletContext) | | #{facesContext} | 当前FacesContext实例 | | #{param} | HTTP请求参数映射 | | #{header} | HTTP请求头映射 | | #{session} | HTTP会话对象 | | #{viewScope} | 视图作用域变量映射 |

遍历请求头的示例:

<c:forEach items="#{header}" var="header">
   <tr>
       <td>#{header.key}</td>
       <td>#{header.value}</td>
   </tr>
</c:forEach>

4. EL实战应用场景

4.1. 页面标记集成

在标准HTML中使用:

<meta name="description" content="#{ELBean.pageDescription}"/>

4.2. JavaScript交互

动态生成JS变量:

<script type="text/javascript"> 
  var theVar = #{ELBean.firstName};
</script>

4.3. 布尔运算符

支持高级比较操作:

  • eq 等于(==)
  • lt 小于(<)
  • gt 大于(>)
  • le 小于等于(<=)
  • ge 大于等于(>=)

4.4. 后端Bean求值

在托管Bean中动态解析EL:

FacesContext ctx = FacesContext.getCurrentInstance(); 
Application app = ctx.getApplication(); 
String firstName = app.evaluateExpressionGet(ctx, "#{firstName.value}", String.class); 
HtmlInputText firstNameTextBox = app.evaluateExpressionGet(ctx, "#{firstName}", HtmlInputText.class);

开发者福利:轻松获取页面组件或隐式对象,实现深度交互

5. EL的限制与陷阱

5.1. 不支持方法重载

当Bean存在重载方法时:

public void save(User theUser);
public void save(String username);
public void save(Integer uid);

以下表达式将产生未定义行为:

<h:commandButton value="Save" action="#{ELBean.save(firstName.value)}"/>

原因:ELResolver依赖Class.getMethods()的返回顺序,而该顺序不确定

5.2. 禁用常量与枚举

EL 3.0前无法访问:

public static final String USER_ERROR_MESS = "No, you can’t do that";
enum Days { Sat, Sun, Mon, Tue, Wed, Thu, Fri };

以下用法会报错:

<h:outputText id="message" value="#{ELBean.USER_ERROR_MESS}"/>
<h:commandButton id="saveButton" value="save" rendered="bean.offDay==Days.Sun"/>

5.3. 缺乏空安全机制

当对象为null时:

Hello Mr, #{ELBean.person.surname}"

将直接抛出NPE,没有类似Groovy的安全导航操作符

6. 总结

JSF EL作为视图与后端的粘合剂,具备:

  • 核心价值:无缝连接XHTML视图与Java后端
  • 主要优势:支持双向绑定、方法调用、隐式对象访问
  • 历史局限:旧版本存在重载/常量/空安全等问题

虽然3.0版本已解决部分限制,但在实际开发中仍需注意这些历史遗留问题。完整示例代码请参考GitHub仓库


原始标题:Introduction to JSF EL