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仓库。