JSF Converter似乎是在xhtml页面上的任何其他托管bean之前调用的。它甚至是用rendered=false
在h:selectOneMenu
组件上创建的。
我创建了3个托管bean来测试初始化序列,托管bean按顺序初始化,它们出现在xhtml中,但在它们之前,JSF转换器是创建的,尽管它是最后一个。
表单
<h:form>
<h:inputText value="#{creationTime1.string1}"/>
<h:inputText value="#{creationTime3.string3}"/>
<h:inputText value="#{creationTime2.string2}"/>
<h:selectOneMenu value="#{creationTime1.competitor}" rendered="false" converter="#{testConverter}" >
<f:selectItem itemValue="#{null}" itemLabel="#{msg.none}" />
<f:selectItems value="#{creationTime1.competitorList}" var="competitor" itemValue="#{competitor}"
itemLabel="#{competitor.idCompetitor}"/>
</h:selectOneMenu>
<h:commandButton value="GO" action="#{creationTime1.submit}"/>
</h:form>
转换器
@Named(value = "testConverter")
@ViewScoped
public class TestConverter implements Converter, Serializable {
@PostConstruct
private void init() {
System.out.println(System.currentTimeMillis() + " || TestConverter init");
}
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
...
}
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
...
}
}
托管Bean(所有名称相同但不同)
@Named(value = "creationTime2")
@ViewScoped
public class CreationTime2 implements Serializable {
private String string2;
public String getString2() {
System.out.println("getter srting2");
return string2;
}
public void setString2(String string2) {
this.string2 = string2;
}
@PostConstruct
private void init() {
System.out.println(System.currentTimeMillis() + " || CreationTime2 init");
}
}
页面访问结果
Info: START PHASE RESTORE_VIEW 1
Info: END PHASE RESTORE_VIEW 1
Info: START PHASE RENDER_RESPONSE 6
Info: 1451147920374 || TestConverter init
Info: 1451147920401 || CreationTime1 init
Info: getter string1
Info: 1451147920407 || CreationTime3 init
Info: getter string3
Info: 1451147920414 || CreationTime2 init
Info: getter string2
Info: END PHASE RENDER_RESPONSE 6
我还尝试了使用@FacesConverter
而不是CDIBean,结果是相同的。
- 为什么转换器在任何其他bean之前被调用
- 为什么使用
rendered=false
创建转换器?难道它不应该像不存在一样通过h:celectOneMenu
而不创建自己吗 - 我能以某种方式让转换器在这3个bean之后创建自己吗(按照它在xhtml页面上出现的顺序)
使用
- Mojarra 2.2.7
- GlassFish 4.1
为什么转换器在任何其他bean之前被调用
转换器并不完全是";调用";即getAsObject()
和getAsString()
在该时刻都没有被调用。它只是在构建视图的过程中被实例化,然后被指定为父ValueHolder
组件的属性。JSF对组件上声明的每个转换器、验证器和(ajax)行为都执行此操作。
转换器实际上是一个托管bean,这并没有什么区别。这只是一个技巧,以便能够在其中注入a.o.EJB。JSF检查该值是文本字符串还是EL表达式。如果是文字字符串,则将其视为创建转换器实例的转换器ID,否则,如果EL表达式返回一个具体的Converter
实例,则直接使用它。或者,如果什么都没有,那么JSF将通过匹配value
类型(如果有的话)的目标类来创建转换器。
为什么使用
rendered=false
创建转换器
因为rendered
属性仅在视图渲染时计算,而不是在视图构建时计算。
我能以某种方式让转换器在这3个bean之后创建自己吗(按照它在xhtml页面上的显示顺序)
否,但是如果";不必要的";转换器的实例化是您关心的问题,然后您可以使用JSTL来有条件地添加转换器。
<h:selectOneMenu ... rendered="#{bean.condition}">
<c:if test="#{bean.condition}"><f:converter binding="#{testConverter}" /></c:if>
...
</h:selectOneMenu>
您只需要记住,<x:someComponent rendered>
和<c:if test>
不会同时进行评估。<c:if test>
在视图构建时间期间评估,<x:someComponent rendered>
在视图渲染时间期间评估。因此,如果由于某种原因,条件在这些时刻之间发生了变化,那么您基本上需要重建视图(即显式导航到同一视图,而不是返回void
/null
)。如果在重新创建bean时将条件绑定到视图范围的bean属性,那么这可能会变得很糟糕。您基本上需要在@PostConstruct
期间保留该条件。
尽管如此,无论如何实例化的转换器(以及验证器和行为)应该是您最不关心的问题。然而,如果在实例化方面存在性能问题,或者在实例化排序方面存在技术问题,那么您最好根据具体的功能需求寻找不同的解决方案。
另请参阅:
- JSF2 Facelets中的JSTL。。。有道理吗