为什么 JSF 转换器在任何其他托管 bean 之前创建?
Why is JSF converter created before any other managed bean?
JSF 转换器似乎在 xhtml 页面上的任何其他托管 bean 之前被调用。它甚至是在 h:selectOneMenu
组件上使用 rendered=false
创建的。
我已经创建了 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) {
...
}
}
Managed Beans(同名不同)
@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,结果是一样的。
- 为什么在任何其他 bean 之前调用转换器?
- 为什么要用
rendered=false
创建转换器?难道它不应该只是通过 h:celectOneMenu
就像它不存在并且不创建自己一样吗?
- 我能否以某种方式让转换器在这 3 个 bean 之后创建自己(按照它出现在 xhtml 页面上的顺序)?
使用
- Mojarra 2.2.7
- GlassFish 4.1
Why is converter invoked before any other bean?
此处并未完全“调用”转换器,即此时既未调用 getAsObject()
也未调用 getAsString()
。它只是在 and then assigned as a property of the parent ValueHolder
组件期间实例化。 JSF 为组件上声明的每个转换器、验证器和 (ajax) 行为执行此操作。
实际上作为托管 bean 的转换器在这方面没有区别。这只是为了能够注入 a.o 的一个技巧。其中有一个 EJB。 JSF 检查该值是文字字符串还是 EL 表达式。如果是文字字符串,则将其视为 converter ID to create the converter instance, else if the EL expression returns a concrete Converter
instance, then it's directly used. Or if there's nothing, then JSF will create the converter by target class 匹配 value
类型(如果有的话)。
Why is converter created with rendered=false
?
因为 rendered
属性仅在视图渲染期间计算,而不是在视图构建期间计算。
Can I somehow make converter create itself after these 3 beans (in order it appears on the xhtml page)?
不,但是如果您担心转换器的“不必要”实例化,那么您可以使用 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
.
期间保留条件
尽管如此,无论如何,实例化的转换器(以及验证器和行为)应该是您最不关心的。但是,如果在实例化方面存在性能问题,或者在实例化顺序方面存在技术问题,那么您最好根据具体的功能要求寻找不同的解决方案。
另请参阅:
- JSTL in JSF2 Facelets... makes sense?
JSF 转换器似乎在 xhtml 页面上的任何其他托管 bean 之前被调用。它甚至是在 h:selectOneMenu
组件上使用 rendered=false
创建的。
我已经创建了 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) {
...
}
}
Managed Beans(同名不同)
@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,结果是一样的。
- 为什么在任何其他 bean 之前调用转换器?
- 为什么要用
rendered=false
创建转换器?难道它不应该只是通过h:celectOneMenu
就像它不存在并且不创建自己一样吗? - 我能否以某种方式让转换器在这 3 个 bean 之后创建自己(按照它出现在 xhtml 页面上的顺序)?
使用
- Mojarra 2.2.7
- GlassFish 4.1
Why is converter invoked before any other bean?
此处并未完全“调用”转换器,即此时既未调用 getAsObject()
也未调用 getAsString()
。它只是在 ValueHolder
组件期间实例化。 JSF 为组件上声明的每个转换器、验证器和 (ajax) 行为执行此操作。
实际上作为托管 bean 的转换器在这方面没有区别。这只是为了能够注入 a.o 的一个技巧。其中有一个 EJB。 JSF 检查该值是文字字符串还是 EL 表达式。如果是文字字符串,则将其视为 converter ID to create the converter instance, else if the EL expression returns a concrete Converter
instance, then it's directly used. Or if there's nothing, then JSF will create the converter by target class 匹配 value
类型(如果有的话)。
Why is converter created with
rendered=false
?
因为 rendered
属性仅在视图渲染期间计算,而不是在视图构建期间计算。
Can I somehow make converter create itself after these 3 beans (in order it appears on the xhtml page)?
不,但是如果您担心转换器的“不必要”实例化,那么您可以使用 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
.
尽管如此,无论如何,实例化的转换器(以及验证器和行为)应该是您最不关心的。但是,如果在实例化方面存在性能问题,或者在实例化顺序方面存在技术问题,那么您最好根据具体的功能要求寻找不同的解决方案。
另请参阅:
- JSTL in JSF2 Facelets... makes sense?