为什么在任何其他托管bean之前创建JSF转换器?

时间:2015-12-26 16:59:11

标签: jsf converter cdi

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,结果是一样的。

  1. 为什么在任何其他bean之前调用转换器?
  2. 为什么使用rendered=false创建转换器?它不应该只是通过h:celectOneMenu,因为它不存在并且不能创造它自己吗?
  3. 我可以以某种方式让转换器在这3个bean之后自己创建(按顺序出现在xhtml页面上)吗?
    • Mojarra 2.2.7
    • GlassFish 4.1

1 个答案:

答案 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期间保留条件。

尽管如此,无论如何要实例化的转换器(以及验证器和行为)应该是您最不关心的问题。如果在实例化方面遇到性能损失,或者实例化排序存在技术问题,那么根据具体的功能要求,您最好寻找不同的解决方案。

另见: