Pregunta

Digamos que especifique un componente outputText como esto:

<h:outputText value="#{ManagedBean.someProperty}"/>

Si se imprime un mensaje de registro cuando el captador de someProperty se llama y se carga la página, es trivial darse cuenta de que el captador está siendo llamado más de una vez por solicitud (dos veces o tres veces lo que sucedió en mi caso):

DEBUG 2010-01-18 23:31:40,104 (ManagedBean.java:13) - Getting some property
DEBUG 2010-01-18 23:31:40,104 (ManagedBean.java:13) - Getting some property

Si el valor de someProperty es caro para el cálculo, esto puede ser potencialmente un problema.

Googled un poco y pensamos que este es un problema conocido. Una solución era incluir un cheque y ver si ya se había calculado:

private String someProperty;

public String getSomeProperty() {
    if (this.someProperty == null) {
        this.someProperty = this.calculatePropertyValue();
    }
    return this.someProperty;
}

El principal problema con esto es que se obtiene un montón de código repetitivo, por no hablar de las variables privadas que puede que no necesite.

¿Cuáles son las alternativas a este enfoque? ¿Hay una manera de lograr esto sin mucho código innecesario? ¿Hay una manera de detener JSF se comporte de esta manera?

Gracias por su ayuda!

¿Fue útil?

Solución

Esto es causado por la naturaleza de las expresiones diferida #{} (tenga en cuenta que las expresiones "legado" estándar ${} comportan exactamente lo mismo cuando se utiliza en lugar de Facelets JSP). La expresión diferida no es inmediatamente evaluado, pero crea como un ValueExpression objeto y el método de obtención detrás de la expresión se ejecuta cada vez que cuando el código llama ValueExpression#getValue() .

Esto normalmente será invocado una o dos veces por ciclo JSF de petición-respuesta, dependiendo de si el componente es un componente de entrada o de salida ( aprender aquí ). Sin embargo, este conteo se puede levantar (mucho) más alto cuando se utiliza en la iteración de componentes JSF (como <h:dataTable> y <ui:repeat>), o de aquí para allá en una expresión booleana como el atributo rendered. JSF (en concreto, EL) no almacenar en caché el resultado evaluado de la expresión EL en absoluto, ya que puede devolver valores diferentes en cada llamada (por ejemplo, cuando es dependiente de la fila tabla de datos actualmente iterada).

La evaluación de una expresión EL e invocar un método getter es una operación muy barato, por lo que en general no debe preocuparse acerca de esto en absoluto. Sin embargo, la historia cambia cuando se está realizando caro lógica / negocio DB en el método de obtención por alguna razón. Esto se vuelve a ejecutar cada vez!

Los métodos Getter en beans de respaldo JSF deben ser diseñados de esa manera que únicamente rendimiento la propiedad ya preparado, y nada más, exactamente según la Javabeans especificación . Que no deben hacer ninguna lógica DB / negocio caro en absoluto. Para que y / o (acción) métodos oyente @PostConstruct del bean deben ser utilizados. Se ejecutan una vez en algún momento del ciclo de vida basado en la petición de JSF y eso es exactamente lo que quiere.

A continuación se presenta un resumen de todos los diferentes derecho maneras de PRESET / 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 constructor o inicialización bloque de utilizació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 hay por que realmente no hay otras maneras, debido a algunos requisitos de diseño restrictivas, a continuación, debe introducir la carga diferida dentro del método de obtención. Es decir. si la propiedad es null, a continuación, cargar y asignarlo a la propiedad, de lo contrario devolverlo.

    public SomeObject getSomeProperty() {
        // If there are really no other ways, introduce lazy loading.
        if (someProperty == null) {
            someProperty = loadSomeProperty();
        }

        return someProperty;
    }

De esta manera la cara lógica / negocio DB no será innecesariamente ser ejecutado en cada llamada captador sola.

Ver también:

Otros consejos

Con JSF 2.0 se puede adjuntar un oyente a un evento del sistema

<h:outputText value="#{ManagedBean.someProperty}">
   <f:event type="preRenderView" listener="#{ManagedBean.loadSomeProperty}" />
</h:outputText>

También puede incluir la página JSF en una etiqueta f:view

<f:view>
   <f:event type="preRenderView" listener="#{ManagedBean.loadSomeProperty}" />

      .. jsf page here...

<f:view>

He escrito un artículo sobre cómo almacenar en caché JSF habas getter con Spring AOP.

creo un MethodInterceptor sencilla que intercepta todos los métodos anotados con una anotación especial:

public class CacheAdvice implements MethodInterceptor {

private static Logger logger = LoggerFactory.getLogger(CacheAdvice.class);

@Autowired
private CacheService cacheService;

@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {

    String key = methodInvocation.getThis() + methodInvocation.getMethod().getName();

    String thread = Thread.currentThread().getName();

    Object cachedValue = cacheService.getData(thread , key);

    if (cachedValue == null){
        cachedValue = methodInvocation.proceed();
        cacheService.cacheData(thread , key , cachedValue);
        logger.debug("Cache miss " + thread + " " + key);
    }
    else{
        logger.debug("Cached hit " + thread + " " + key);
    }
    return cachedValue;
}


public CacheService getCacheService() {
    return cacheService;
}
public void setCacheService(CacheService cacheService) {
    this.cacheService = cacheService;
}

}

Este interceptor se utiliza en un archivo de configuración de resorte:

    <bean id="advisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
    <property name="pointcut">
        <bean class="org.springframework.aop.support.annotation.AnnotationMatchingPointcut">
            <constructor-arg index="0"  name="classAnnotationType" type="java.lang.Class">
                <null/>
            </constructor-arg>
            <constructor-arg index="1" value="com._4dconcept.docAdvance.jsfCache.annotation.Cacheable" name="methodAnnotationType" type="java.lang.Class"/>
        </bean>
    </property>
    <property name="advice">
        <bean class="com._4dconcept.docAdvance.jsfCache.CacheAdvice"/>
    </property>
</bean>

Esperamos que ayude!

publicado originalmente en PrimeFaces foro @ http://forum.primefaces.org/ viewtopic.php? f = 3 & t = 29546

Recientemente, he estado obsesionado evaluar el rendimiento de mi aplicación, tuning consultas JPA, en sustitución de las consultas SQL dinámico con consultas con nombre, y esta misma mañana, me di cuenta de que un método getter fue más de un PUNTO CALIENTE en Java Visual VM de el resto de mi código (o la mayoría de mi código).

método Getter:

PageNavigationController.getGmapsAutoComplete()

Referenciado por ui: incluir en en index.xhtml

A continuación, se verá que PageNavigationController.getGmapsAutoComplete () es un punto caliente (problema de rendimiento) en Java VM Visual. Si se mira más abajo, en la captura de pantalla, se verá que getLazyModel (), PrimeFaces método getter tabla de datos perezoso, es un punto caliente demasiado, sólo cuando enduser está haciendo un montón de 'perezoso tabla de datos' tipo de cosas / operaciones / tareas en la aplicación. :)

Java VM Visual: mostrando PUNTO CALIENTE

Vea (original) código de abajo.

public Boolean getGmapsAutoComplete() {
    switch (page) {
        case "/orders/pf_Add.xhtml":
        case "/orders/pf_Edit.xhtml":
        case "/orders/pf_EditDriverVehicles.xhtml":
            gmapsAutoComplete = true;
            break;
        default:
            gmapsAutoComplete = false;
            break;
    }
    return gmapsAutoComplete;
}

referenciado por el siguiente en index.xhtml:

<h:head>
    <ui:include src="#{pageNavigationController.gmapsAutoComplete ? '/head_gmapsAutoComplete.xhtml' : (pageNavigationController.gmaps ? '/head_gmaps.xhtml' : '/head_default.xhtml')}"/>
</h:head>

Solución: ya que este es un método 'captador', mover el código y asignar valor a gmapsAutoComplete antes del método que se llama; ver código de abajo.

/*
 * 2013-04-06 moved switch {...} to updateGmapsAutoComplete()
 *            because performance = 115ms (hot spot) while
 *            navigating through web app
 */
public Boolean getGmapsAutoComplete() {
    return gmapsAutoComplete;
}

/*
 * ALWAYS call this method after "page = ..."
 */
private void updateGmapsAutoComplete() {
    switch (page) {
        case "/orders/pf_Add.xhtml":
        case "/orders/pf_Edit.xhtml":
        case "/orders/pf_EditDriverVehicles.xhtml":
            gmapsAutoComplete = true;
            break;
        default:
            gmapsAutoComplete = false;
            break;
    }
}

Resultados de la prueba: PageNavigationController.getGmapsAutoComplete () ya no es un punto caliente en Java VM Visual (ni siquiera se mostrará más)

Compartir este tema, ya que muchos de los usuarios expertos han aconsejado a los desarrolladores JSF jóvenes no agregar código en los métodos getter ''. :)

Si está utilizando CDI, puede utilizar métodos de productores. Se va a llamar muchas veces, pero el resultado de la primera llamada se almacena en caché en el ámbito de la judía y es eficiente para captadores que se Computing o inicializar objetos pesados! Ver aquí , para obtener más información .

Probablemente se podría utilizar AOP para crear algún tipo de Aspecto que almacena en caché los resultados de nuestros captadores para un período de tiempo configurable. Esto le impediría necesidad de copiar y pegar código repetitivo en docenas de métodos de acceso.

  

Si el valor de someProperty es   caro para el cálculo, esto puede   potencialmente ser un problema.

Esto es lo que llamamos una optimización prematura. En el raro caso de que un generador de perfiles le indica que el cálculo de una propiedad es tan extraordinariamente caro que llamar tres veces en lugar de una vez tiene un impacto significativo rendimiento, se agrega el almacenamiento en caché como usted la describe. Pero a menos que hagas algo realmente estúpido como factorización de números primos o acceder a un DATABSE en un captador, su código más probable es que haya una docena peores ineficiencias en lugares a los que nunca he pensado.

Me gustaría también consejos utilizando como marco Primefaces en lugar de acciones JSF, que abordar estas cuestiones antes de JSF equipo electrónico. g en PrimeFaces puede establecer presentar parcial. De lo contrario BalusC ha explicado bien.

Todavía gran problema en JSF. Fo ejemplo, si tiene un isPermittedToBlaBla método para el control de seguridad y en la vista que haya rendered="#{bean.isPermittedToBlaBla} entonces el método será llamado varias veces.

El control de seguridad podría complicarse por ejemplo. Consulta LDAP etc Por lo tanto se debe evitar que con

Boolean isAllowed = null ... if(isAllowed==null){...} return isAllowed?

y debe asegurarse dentro de una sesión de frijol esta por solicitud.

Ich pensar JSF debe implementar aquí algunas extensiones para evitar múltiples llamadas (por ejemplo anotación @Phase(RENDER_RESPONSE) calle este método sólo una vez después de la fase RENDER_RESPONSE ...)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top