与前面的示例相关,我尝试监视服务器上的get/set方法(何时调用,以及调用频率)。所以,我的实际对象是这样的:
@ManagedBean(name="selector")
@RequestScoped
public class Selector {
@ManagedProperty(value="#{param.profilePage}")
private String profilePage;
public String getProfilePage() {
if(profilePage==null || profilePage.trim().isEmpty()) {
this.profilePage="main";
}
System.out.println("GET "+profilePage);
return profilePage;
}
public void setProfilePage(String profilePage) {
this.profilePage=profilePage;
System.out.println("SET "+profilePage);
}
}
唯一可以调用此方法(它仅在呈现时调用get方法)的页面是:
<!DOCTYPE html>
<ui:composition
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:panelGroup layout="block" id="profileContent">
<h:panelGroup rendered="#{selector.profilePage=='main'}">
// nothing at the moment
</h:panelGroup>
</h:panelGroup>
</ui:composition>
当我看到服务器日志时,我昏迷了,我看到:
SET null
GET main
GET main
GET main
GET main
GET main
GET main
GET main
什么?它会调用七次getProfilePage()
方法吗?(还有1次setProfilePage()
)我想知道为什么这种行为:)
谢谢
添加了一个示例
豆子
@ManagedBean(name="selector")
@RequestScoped
public class Selector {
@ManagedProperty(value="#{param.profilePage}")
private String profilePage;
@PostConstruct
public void init() {
if(profilePage==null || profilePage.trim().isEmpty()) {
this.profilePage="main";
}
}
public String getProfilePage() { return profilePage; }
public void setProfilePage(String profilePage) { this.profilePage=profilePage; }
}
profile.xhtml
<h:panelGroup layout="block" id="profileContent">
<h:panelGroup layout="block" styleClass="content_title">
Profilo Utente
</h:panelGroup>
<h:panelGroup rendered="#{selector.profilePage=='main'}">
<ui:include src="/profile/profile_main.xhtml" />
</h:panelGroup>
<h:panelGroup rendered="#{selector.profilePage=='edit'}">
<ui:include src="/profile/profile_edit.xhtml" />
</h:panelGroup>
</h:panelGroup>
// profile_main.xhtml
<h:form id="formProfileMain" prependId="false">
<h:panelGroup layout="block" styleClass="content_span">
<h:outputScript name="jsf.js" library="javax.faces" target="head" />
<h:panelGroup layout="block" styleClass="profilo_3">
<h:commandButton value="EDIT">
<f:setPropertyActionListener target="#{selector.profilePage}" value="edit" />
<f:ajax event="action" render=":profileContent"/>
</h:commandButton>
</h:panelGroup>
</h:panelGroup>
</h:form>
// profile_edit.xhtml
<h:form id="formProfileEdit" prependId="false">
<h:panelGroup layout="block" styleClass="content_span">
<h:outputScript name="jsf.js" library="javax.faces" target="head" />
<h:panelGroup layout="block" styleClass="profilo_3">
<h:commandButton value="Edit">
<f:setPropertyActionListener target="#{selector.profilePage}" value="editProfile" />
<f:ajax event="action" render=":profileContent"/>
</h:commandButton>
<h:commandButton value="Back">
<f:setPropertyActionListener target="#{selector.profilePage}" value="main" />
<f:ajax event="action" render=":profileContent"/>
</h:commandButton>
</h:panelGroup>
</h:panelGroup>
</h:form>
在本例中,我调用了profile_main (默认情况下);之后(例如)调用了profile_edit (通过单击EDIT);之后,通过单击Back返回到profile_main。现在,如果我想重新加载profile_edit (编辑),我需要多次点击该命令按钮。为什么?
发布于 2010-11-26 07:51:22
EL (表达式语言,那些#{}
的东西)不会缓存调用的结果。它只是直接访问bean中的数据。如果getter只是返回数据,这通常不会有什么坏处。
设置器调用由@ManagedProperty
完成。它基本上做了以下工作:
selector.setProfilePage(request.getParameter("profilePage"));
getter调用都是由rendered="#{selector.profilePage == 'some'}"
在呈现响应阶段完成的。在UIComponent#encodeAll()
中,当它第一次计算false
时,将不再执行任何调用。当它对true
求值时,将按以下顺序再次对其求值六次:
UIComponent#encodeBegin()
-定位component.Renderer#encodeBegin()
开头的渲染器-渲染component.UIComponent#encodeChildren()
的开头-定位component.Renderer#encodeChildren()
的子级的渲染器-渲染子component.UIComponent#encodeEnd()
的子级-定位component.Renderer#encodeEnd()
结尾的渲染器-渲染组件的结尾。组件及其渲染器在每个步骤中验证是否允许它渲染。在表单提交期间,如果输入或命令组件或其任何父组件具有rendered
属性,则还将在应用请求值阶段对其进行评估,作为防止篡改/黑客请求的保护措施的一部分。
的确,这看起来既笨拙又低效。根据spec issue 941的说法,它被认为是JSF的阿基里斯愈合。有人建议删除所有这些重复检查,并坚持在UIComponent#encodeAll()
中完成的检查,或者在每个阶段的基础上评估isRendered()
。很明显,问题的根源在于EL,而不是During EG discussion,而且使用CDI可以极大地提高性能。因此,没有必要从JSF规范方面解决这个问题。
如果您关心的是,如果托管属性为null或empty,则只应在其设置后检查一次,那么可以考虑将其移动到一个带有@PostConstruct
注释的方法中。这样的方法将在bean的构造和所有依赖项注入之后直接调用。
@PostConstruct
public void init() {
if (profilePage == null || profilePage.trim().isEmpty()) {
profilePage = "main";
}
}
另请参阅:
发布于 2012-06-11 20:00:18
您可以使用CDI生成器方法。它将被调用多次,但第一次调用的结果被缓存在bean的作用域中,并且对于计算或初始化重对象的getter是有效的!有关更多信息,请参见here。
https://stackoverflow.com/questions/4281261
复制相似问题