jsf jsf-2 facelets getter

jsf - ¿Por qué el captador llama al captador tantas veces?



facelets (2)

En relación con un ejemplo anterior, traté de monitorear mis métodos de obtención / configuración en el servidor (cuando se llaman y con qué frecuencia). Por lo tanto, mi estado real se ve tal:

@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); } }

y la única página que puede llamar a este método (solo llama al método get en render) es:

<!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>

mi estupor cuando veo el registro del servidor, y veo:

SET null GET main GET main GET main GET main GET main GET main GET main

¿Qué? ¿Se llama siete veces el método getProfilePage() ? (y también 1 vez setProfilePage() ) Me gustaría saber por qué este comportamiento :)

Gracias

AÑADIDO UN EJEMPLO

Frijol

@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>

En este ejemplo, llamo a profile_main (por defecto); Después (por ejemplo) llamo a profile_edit (haciendo clic en EDITAR); Después, vuelvo a profile_main haciendo clic en Atrás. Ahora, si quiero volver a cargar profile_edit (EDIT), necesito hacer clic muchas veces en ese botón de comando. ¿Por qué?


EL (lenguaje de expresión, esas #{} cosas) no almacenará en caché el resultado de las llamadas o algo así. Simplemente accede a los datos directamente en el bean. Esto normalmente no hace daño si el getter simplemente devuelve los datos.

La llamada de establecimiento se realiza mediante @ManagedProperty . Básicamente hace lo siguiente:

selector.setProfilePage(request.getParameter("profilePage"));

Todas las llamadas de captador se realizan con rendered="#{selector.profilePage == ''some''}" durante la fase de respuesta de render. Cuando se evalúa como false la primera vez, en UIComponent#encodeAll() , no se harán más llamadas. Cuando se evalúe como true , se volverá a evaluar seis veces más en la siguiente secuencia:

  1. UIComponent#encodeBegin() - Localiza el renderizador para el inicio del componente.
  2. Renderer#encodeBegin() - Renders comienza de componente.
  3. UIComponent#encodeChildren() : localiza el renderizador para los hijos del componente.
  4. Renderer#encodeChildren() : representa los elementos Renderer#encodeChildren() del componente.
  5. UIComponent#encodeEnd() - Localiza el renderizador para el final del componente.
  6. UIComponent#encodeEnd() - Representa el final del componente.

El componente y su renderizador verifican durante cada paso si se les permite renderizar. Durante un envío de formulario, si un componente de entrada o comando o alguno de sus padres tiene un atributo rendered , entonces también se evaluará durante la fase de solicitud de valores como parte de la protección contra las solicitudes manipuladas / pirateadas.

Es cierto, esto parece torpe e ineficiente. Se consideró la cura de Aquiles de JSF según el número de especificaciones 941 . Se ha sugerido eliminar todas esas comprobaciones repetidas y atenerse a la realizada en UIComponent#encodeAll() , o evaluar isRendered() en una base por fase. Durante la discusión de EG , quedó claro que la raíz del problema está en EL, no en JSF, y que el rendimiento podría mejorarse considerablemente con CDI. Así que no había necesidad de resolverlo desde el lado de especificaciones de JSF.

Si le preocupa que la propiedad administrada se debe verificar solo una vez después de su configuración, si está vacía o sin valor, considere moverla a un método que esté anotado con @PostConstruct . Dicho método se llamará directamente después de la construcción del frijol y de toda la inyección de dependencia.

@PostConstruct public void init() { if (profilePage == null || profilePage.trim().isEmpty()) { profilePage = "main"; } }

Ver también:


Puedes usar los métodos de los productores de CDI. Se llamará muchas veces, pero el resultado de la primera llamada se almacena en caché en el alcance del bean y es eficiente para los captadores que están computando o inicializando objetos pesados. Vea here , para más información.