
本文详解如何使用 JavaServer Faces(JSF)与 @SessionScoped 管理 Bean 构建一个功能完整的图书购物车应用,涵盖单选列表绑定、会话级数据持久化、购物车页面展示及总价计算,并修复常见 EL 表达式错误。
本文详解如何使用 javaserver faces(jsf)与 `@sessionscoped` 管理 bean 构建一个功能完整的图书购物车应用,涵盖单选列表绑定、会话级数据持久化、购物车页面展示及总价计算,并修复常见 el 表达式错误。
在 Java EE Web 开发中,JSF 是构建服务端驱动表单应用的成熟框架。本教程将指导你实现一个轻量但结构清晰的图书购物车系统:用户可在首页通过 <selectoneradio></selectoneradio> 选择图书,提交后添加至会话级购物车;多次访问首页可追加选择;点击链接跳转至购物车页,显示所选图书、单价及汇总金额。
✅ 关键问题修复:EL 表达式中的 Size 错误
你在 index.xhtml 中写了:
<p>You have selected #{selectionsBean.Size} book(s).</p>这会导致运行时异常:The class 'sessiontracking.SelectionsBean' does not have the property 'Size'。
原因在于:JSF 的统一表达式语言(EL)不支持直接调用 Java 方法名(如 size())且首字母大写;它仅识别符合 JavaBean 规范的 getter 方法(如 getSize()),而你的 SelectionsBean 并未定义该方法,且 selections 是 List<string></string> 字段,其 .size() 是实例方法,非属性。
✅ 正确写法(利用 EL 对集合方法的支持):
立即学习“Java免费学习笔记(深入)”;
<p>You have selected #{selectionsBean.selections.size()} book(s).</p>✅ 注意:JSF 2.0+ 支持在 EL 中直接调用集合对象的方法(如
.size(),.isEmpty()),前提是该对象非 null。因此需确保selections在构造器中已初始化(你已做到),否则需添加空值防护:<p>You have selected #{empty selectionsBean.selections ? 0 : selectionsBean.selections.size()} book(s).</p>
? 完整可运行代码优化建议
1. 修正 SelectionsBean.java —— 补全缺失逻辑与健壮性
当前类缺少 selectedBook 属性及其 getter/setter,导致 <selectoneradio value="#{selectionsBean.selectedBook}"></selectoneradio> 绑定失败。同时,addToCart() 尚未被 JSF 动作调用。需补充如下:
// 新增字段
private String selectedBook;
// 添加 getter/setter
public String getSelectedBook() { return selectedBook; }
public void setSelectedBook(String selectedBook) { this.selectedBook = selectedBook; }
// 修改 submit 操作:在 JSF action 方法中调用
public String submitSelection() {
if (selectedBook != null && !selectedBook.trim().isEmpty()) {
// 映射 itemValue → 图书标题(示例采用简单映射,生产环境建议用枚举或 Map)
String title = switch (selectedBook) {
case "java" -> "Java How to Program";
case "cpp" -> "C++ How to Program";
case "iphone" -> "iPhone for Programmers: An App-Driven Approach";
case "android" -> "Android for Programmers: An App-Driven Approach";
default -> null;
};
if (title != null) {
addToCart(title);
}
}
return null; // stay on same page
}并在 index.xhtml 中绑定按钮动作:
<h:commandButton value="Submit" action="#{selectionsBean.submitSelection}"/>2. 修正视图层:cart.xhtml 使用正确 Bean 名称
你当前 cart 页面引用了 #{cartBean.cart},但实际管理类名为 SelectionsBean,且未提供 cart 属性。应统一使用 selectionsBean,并调整数据展示逻辑:
<!-- cart.xhtml -->
<h:dataTable value="#{selectionsBean.selections}" var="title">
<h:column>
<f:facet name="header">Book Title</f:facet>
#{title}
</h:column>
<h:column>
<f:facet name="header">Price</f:facet>
$#{selectionsBean.pricesMap[title]}
</h:column>
</h:dataTable>
<p><strong>Total price: $#{selectionsBean.totalPrice}</strong></p>? 提示:为支持 EL 中
pricesMap[title]访问,需在SelectionsBean中暴露pricesMap(添加 public getter):public Map<String, Double> getPricesMap() { return pricesMap; }
3. 最佳实践补充
-
避免硬编码映射:
itemValue与图书标题的转换建议封装为Map<string string></string>或使用SelectItem列表动态生成。 -
价格精度:金额应使用
BigDecimal而非double防止浮点误差(教学场景可简化,生产必改)。 -
会话清理:可添加“清空购物车”按钮,调用
selections.clear()。 -
JSF 版本兼容性:上述代码适用于 JSF 2.x;若用 Jakarta EE 9+(如 JSF 3.0),需更新包名(
jakarta.faces.bean.*→ 已废弃,推荐改用 CDI@Named @SessionScoped)。
✅ 总结
本购物车应用的核心在于:
? 利用 @SessionScoped 使 SelectionsBean 跨请求保持状态;
? 通过 List<string></string> 存储选择项,配合 Map<string double></string> 实现标题-价格映射;
? 在 XHTML 中正确使用 EL 表达式(如 #{bean.list.size()}),并规避非法属性访问;
? 所有交互均通过 JSF 标准组件(<form></form>, <commandbutton></commandbutton>)驱动,无需手动处理 HTTP 参数。
完成以上修改后,应用即可稳定运行:用户反复选择、跳转查看、实时计算总价——一个符合企业级 JSF 开发规范的入门级购物车就此诞生。


















