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) {
...
}
}
托管豆(所有名称都相同)
@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
代替CDI bean,结果是一样的。
rendered=false
创建转换器?它不应该只是通过h:celectOneMenu
,因为它不存在并且不能创造它自己吗?是
答案 0 :(得分:5)
为什么在任何其他bean之前调用转换器?
这里没有完全“调用”转换器,即此时不调用getAsObject()
和getAsString()
。它只是在building the view期间实例化,然后被指定为父ValueHolder
组件的属性。 JSF为组件上声明的每个转换器,验证器和(ajax)行为执行此操作。
转换器实际上是一个托管bean在这方面没有区别。这只是一个技巧,以便能够注入a.o.其中的EJB。 JSF检查值是文字字符串还是EL表达式。如果是文字字符串,则将其视为converter ID to create the converter instance,否则如果EL表达式返回具体的Converter
实例,则直接使用它。或者如果没有,那么JSF将create the converter by target class匹配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
期间保留条件。
尽管如此,无论如何要实例化的转换器(以及验证器和行为)应该是您最不关心的问题。如果在实例化方面遇到性能损失,或者实例化排序存在技术问题,那么根据具体的功能要求,您最好寻找不同的解决方案。