¿Por qué se llama tantas veces el getter al atributo renderizado?

Relacionado con un ejemplo anterior, intenté monitorear mis métodos get / set en el servidor (cuándo se llaman y con qué frecuencia). Entonces, mi aspecto actual es 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 renderizado) es:

     // nothing at the moment    

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é? ¿Llama siete veces al método getProfilePage() ? (y también 1 vez setProfilePage() ) Me gustaría saber por qué este comportamiento 🙂

Gracias

AGREGADO 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

   Profilo Utente         // profile_main.xhtml            // profile_edit.xhtml                

En este ejemplo, llamo al profile_main (por defecto); Después (por ejemplo) llamo profile_edit (haciendo clic en EDIT); 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, esos #{} elementos) no almacenará en caché el resultado de las llamadas más o menos. Simplemente accede a los datos directamente en el bean. Esto normalmente no daña si el getter simplemente devuelve los datos.

La llamada setter se realiza por @ManagedProperty . Básicamente hace lo siguiente:

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

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

  1. UIComponent#encodeBegin() – Busca el renderer para el comienzo del componente.
  2. Renderer#encodeBegin() – Renders comienza el componente.
  3. UIComponent#encodeChildren() – Localiza el renderizador para 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. Renderer#encodeEnd() – Renderiza el final del componente.

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

Es cierto, esto parece torpe e ineficiente. Se consideró la curación de Aquiles de JSF según la especificación 941 . Se ha sugerido eliminar todas esas comprobaciones repetidas y mantener el hecho en UIComponent#encodeAll() , o para evaluar isRendered() por fases. 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 mucho con CDI. Entonces no hubo necesidad de resolverlo desde el lado de las especificaciones JSF.


Si le preocupa que la propiedad administrada se verifique solo una vez después de su configuración, si es nula o está vacía, considere moverla a un método anotado con @PostConstruct . Se llamará a dicho método directamente después de la construcción del bean y de toda la dependency injection.

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

Ver también:

  • ¿Por qué JSF llama getters varias veces?

puedes usar métodos de Productores CDI. Se llamará muchas veces, pero el resultado de la primera llamada se almacena en caché en el scope del bean y es eficiente para getters que están computando o inicializando objetos pesados. Mira aquí , para más información.