如何在Jakarta Faces中实例化Bean属性供后续页面使用?
解决Jakarta Faces跨页面UIInput绑定空指针问题
你当前遇到的核心问题,本质是违背了JSF的生命周期设计原则:将UIInput这类属于请求视图生命周期的UI组件,绑定到了SessionScoped的Bean中。UI组件是请求级别的实例,每个页面的视图树都是独立的,强行把它们存在会话作用域里,必然会引发空指针、视图状态混乱等问题。
正确解决方案:分离业务数据与UI组件
绝对不要绑定UI组件实例,直接绑定业务数据(字符串、基本类型或POJO)才是符合规范的做法。以下两种方案都能彻底解决你的问题:
方案1:单SessionScoped Bean存储业务数据
修改Bean,去掉UIInput字段,换成业务数据类型:
@Named @SessionScoped public class Bean1 implements Serializable { private String in1Value; private String in2Value; public void update() { // 根据in1的值生成in2的内容 in2Value = "Yo!!"; } // getter和setter public String getIn1Value() { return in1Value; } public void setIn1Value(String in1Value) { this.in1Value = in1Value; } public String getIn2Value() { return in2Value; } public void setIn2Value(String in2Value) { this.in2Value = in2Value; } }
第一页页面(无需隐藏组件):
<h:body> <div>Bakedbean testing</div> <h:form id="form0"> <h:outputLabel for="rltd" value="Arbitrary input" /> <h:inputText id="rltd" value="#{bean1.in1Value}" valueChangeListener="#{bean1.update}" immediate="true" required="true"> </h:inputText> </h:form> <h:button id="next" value="Next" outcome="/next.xhtml" /> </h:body>
第二页页面:
<h:body> <div>Next</div> <h:form id="next2"> <h:outputLabel for="related">in2 instantiated?</h:outputLabel> <p> <h:inputTextarea id="related" style="border: 1px solid red;" rows="2" cols="25" value="#{bean1.in2Value}"> </h:inputTextarea> </p> </h:form> </h:body>
方案2:拆分RequestScoped与SessionScoped Bean(你提到的方案)
这个方案完全合理,不存在你担心的内存或并发问题,只要SessionScoped Bean只存储轻量级业务数据即可:
- RequestScoped的Bean1负责第一页的输入处理,生成数据后传递给SessionScoped Bean
- SessionScoped的Bean2专门存储跨页面的业务数据
代码示例:
@Named @RequestScoped public class Bean1 implements Serializable { private String in1Value; @Inject private Bean2 bean2; public void update() { // 生成in2的值并存入Bean2 bean2.setIn2Value("Yo!!"); } // getter和setter public String getIn1Value() { return in1Value; } public void setIn1Value(String in1Value) { this.in1Value = in1Value; } }
@Named @SessionScoped public class Bean2 implements Serializable { private String in2Value; // getter和setter public String getIn2Value() { return in2Value; } public void setIn2Value(String in2Value) { this.in2Value = in2Value; } }
第一页页面:
<h:body> <div>Bakedbean testing</div> <h:form id="form0"> <h:outputLabel for="rltd" value="Arbitrary input" /> <h:inputText id="rltd" value="#{bean1.in1Value}" valueChangeListener="#{bean1.update}" immediate="true" required="true"> </h:inputText> </h:form> <h:button id="next" value="Next" outcome="/next.xhtml" /> </h:body>
第二页页面:
<h:body> <div>Next</div> <h:form id="next2"> <h:outputLabel for="related">in2 instantiated?</h:outputLabel> <p> <h:inputTextarea id="related" style="border: 1px solid red;" rows="2" cols="25" value="#{bean2.in2Value}"> </h:inputTextarea> </p> </h:form> </h:body>
为什么原方案不规范?
- 绑定UIInput到SessionScoped Bean违反JSF设计:UI组件是请求级别实例,会话作用域会保留过时的组件实例,引发视图状态异常
- 用
rendered=false的隐藏组件强制实例化UIInput,会在视图树中留下无用节点,增加渲染开销,还可能引发后续页面的组件ID冲突或状态覆盖
关于内存与并发的说明
只要SessionScoped Bean只存储轻量级业务数据(如字符串、基本类型),就不会有明显内存问题。并发方面,每个用户的会话是独立的,同一用户的请求默认串行处理,只要Bean状态是用户专属的,就不会有并发冲突。如果需要处理多窗口场景,可以考虑使用@ViewScoped(Glassfish 7完全支持该作用域与@Named的配合)。
内容的提问来源于stack exchange,提问作者user17022777
相关产品推荐
相关产品推荐

