Esto se debe a la naturaleza de las expresiones diferidas #{}
(tenga en cuenta que las expresiones estándar "heredadas" se ${}
comportan exactamente igual cuando se usan Facelets en lugar de JSP). La expresión diferida no se evalúa de inmediato , sino que se crea como un ValueExpression
objeto y el método getter detrás de la expresión se ejecuta cada vez que se llama el código ValueExpression#getValue()
.
Esto normalmente se invocará una o dos veces por ciclo de solicitud-respuesta JSF, dependiendo de si el componente es un componente de entrada o salida ( aprende aquí ). Sin embargo, este recuento puede aumentar (mucho) más cuando se usa para iterar componentes JSF (como <h:dataTable>
y <ui:repeat>
), o aquí y allá en una expresión booleana como el rendered
atributo. JSF (específicamente, EL) no almacenará en caché el resultado evaluado de la expresión EL ya que puede devolver diferentes valores en cada llamada (por ejemplo, cuando depende de la fila de tabla de datos iterada actualmente).
Evaluar una expresión EL e invocar un método getter es una operación muy barata, por lo que generalmente no debe preocuparse por esto en absoluto. Sin embargo, la historia cambia cuando se realiza una costosa lógica de DB / negocio en el método getter por alguna razón. ¡Esto se volvería a ejecutar cada vez!
Los métodos Getter en beans de respaldo JSF deben diseñarse de tal manera que solo devuelvan la propiedad ya preparada y nada más, exactamente según la especificación Javabeans . No deberían hacer ninguna lógica de DB / negocio costosa en absoluto. Para eso se @PostConstruct
deben usar los métodos de escucha del bean y / o (acción). Se ejecutan solo una vez en algún momento del ciclo de vida JSF basado en solicitudes y eso es exactamente lo que desea.
Aquí hay un resumen de todas las formas correctas de preestablecer / cargar una propiedad.
public class Bean {
private SomeObject someProperty;
@PostConstruct
public void init() {
// In @PostConstruct (will be invoked immediately after construction and dependency/property injection).
someProperty = loadSomeProperty();
}
public void onload() {
// Or in GET action method (e.g. <f:viewAction action>).
someProperty = loadSomeProperty();
}
public void preRender(ComponentSystemEvent event) {
// Or in some SystemEvent method (e.g. <f:event type="preRenderView">).
someProperty = loadSomeProperty();
}
public void change(ValueChangeEvent event) {
// Or in some FacesEvent method (e.g. <h:inputXxx valueChangeListener>).
someProperty = loadSomeProperty();
}
public void ajaxListener(AjaxBehaviorEvent event) {
// Or in some BehaviorEvent method (e.g. <f:ajax listener>).
someProperty = loadSomeProperty();
}
public void actionListener(ActionEvent event) {
// Or in some ActionEvent method (e.g. <h:commandXxx actionListener>).
someProperty = loadSomeProperty();
}
public String submit() {
// Or in POST action method (e.g. <h:commandXxx action>).
someProperty = loadSomeProperty();
return "outcome";
}
public SomeObject getSomeProperty() {
// Just keep getter untouched. It isn't intented to do business logic!
return someProperty;
}
}
Tenga en cuenta que usted debe no utilizar el bloque constructor o inicialización de frijol para el trabajo, ya que puede ser invocado varias veces si estás utilizando un marco de gestión de frijol que utiliza servidores proxy, como CDI.
Si realmente no hay otras formas para usted, debido a algunos requisitos de diseño restrictivos, entonces debe introducir una carga lenta dentro del método getter. Es decir, si la propiedad es null
, entonces cárguela y asígnela a la propiedad, de lo contrario devuélvala.
public SomeObject getSomeProperty() {
// If there are really no other ways, introduce lazy loading.
if (someProperty == null) {
someProperty = loadSomeProperty();
}
return someProperty;
}
De esta manera, la costosa lógica de DB / negocio no se ejecutará innecesariamente en cada llamada getter.
Ver también:
FacesContext#getCurrentPhaseId()
.Con JSF 2.0 puede adjuntar un oyente a un evento del sistema
Alternativamente, puede encerrar la página JSF en una
f:view
etiquetafuente
He escrito un artículo sobre cómo almacenar en caché el generador de beans JSF con Spring AOP.
Creo un simple
MethodInterceptor
que intercepta todos los métodos anotados con una anotación especial:Este interceptor se usa en un archivo de configuración de resorte:
¡Espero que ayude!
fuente
Publicado originalmente en el foro PrimeFaces @ http://forum.primefaces.org/viewtopic.php?f=3&t=29546
Recientemente, he estado obsesionado evaluando el rendimiento de mi aplicación, ajustando consultas JPA, reemplazando consultas SQL dinámicas con consultas con nombre, y esta mañana, reconocí que un método getter era más un HOT SPOT en Java Visual VM que el resto de mi código (o la mayoría de mi código).
Método Getter:
Referenciado por ui: incluir en index.xhtml
A continuación, verá que PageNavigationController.getGmapsAutoComplete () es un HOT SPOT (problema de rendimiento) en Java Visual VM. Si mira más abajo, en la captura de pantalla, verá que getLazyModel (), método de obtención de tabla de datos diferida de PrimeFaces, también es un punto caliente, solo cuando el usuario final está haciendo un montón de cosas / operaciones / tareas de 'tabla de datos diferida' en la aplicación :)
Ver el código (original) a continuación.
Referenciado por lo siguiente en index.xhtml:
Solución: dado que este es un método 'getter', mueva el código y asigne valor a gmapsAutoComplete antes de llamar al método; Ver el código a continuación.
Resultados de la prueba: PageNavigationController.getGmapsAutoComplete () ya no es un HOT SPOT en Java Visual VM (ya ni siquiera aparece)
Compartiendo este tema, ya que muchos de los usuarios expertos han aconsejado a los desarrolladores junior de JSF que NO agreguen código en los métodos 'getter'. :)
fuente
Si está utilizando CDI, puede utilizar los métodos de Productores. 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 calculando o inicializando objetos pesados. Ver aquí , para más información.
fuente
Probablemente podría usar AOP para crear algún tipo de Aspecto que almacenara en caché los resultados de nuestros captadores durante un período de tiempo configurable. Esto evitaría que necesite copiar y pegar código repetitivo en docenas de accesores.
fuente
Esto es lo que llamamos una optimización prematura. En el raro caso de que un generador de perfiles le diga que el cálculo de una propiedad es tan extraordinariamente costoso que llamarlo tres veces en lugar de una vez tiene un impacto significativo en el rendimiento, agrega almacenamiento en caché como lo describe. Pero a menos que haga algo realmente estúpido como factorizar números primos o acceder a una base de datos en un captador, su código probablemente tenga una docena de ineficiencias peores en lugares en los que nunca pensó.
fuente
También recomendaría usar un Marco como Primefaces en lugar de JSF de stock, que abordan dichos problemas ante el equipo de JSF e. g en primefaces puede configurar el envío parcial. De lo contrario, BalusC lo ha explicado bien.
fuente
Sigue siendo un gran problema en JSF. Por ejemplo, si tiene un método
isPermittedToBlaBla
para las comprobaciones de seguridad y, en su opinión, tienerendered="#{bean.isPermittedToBlaBla}
, el método se llamará varias veces.El control de seguridad puede ser complicado, por ejemplo. Consulta LDAP, etc. Por lo tanto, debe evitar eso con
y debe asegurarse dentro de un bean de sesión esto por solicitud.
Creo que JSF debe implementar aquí algunas extensiones para evitar múltiples llamadas (por ejemplo, la anotación llama a
@Phase(RENDER_RESPONSE)
este método solo una vez después de laRENDER_RESPONSE
fase ...)fuente