¿Cómo fuerzo un bean con ámbito de aplicación a crear instancias al iniciar la aplicación?


Parece que no puedo encontrar una manera de forzar que un bean administrado con ámbito de aplicación se instancie/inicialice cuando se inicia la aplicación web. Parece que los frijoles con alcance de aplicación se instancian perezosamente la primera vez que se accede al frijol, no cuando se inicia la aplicación web. Para mi aplicación web esto sucede cuando el primer usuario abre una página en la aplicación web por primera vez.

La razón por la que quiero evitar esto es porque una serie de operaciones de base de datos que consumen mucho tiempo ocurren durante el inicialización de mi aplicación-scoped bean. Tiene que recuperar un montón de datos del almacenamiento persistente y luego almacenar en caché algunos de ellos que se mostrarán con frecuencia al usuario en forma de elementos ListItem, etc. No quiero que todo eso suceda cuando el primer usuario se conecte y, por lo tanto, cause un largo retraso.

Mi primer pensamiento fue usar un método ServletContextListener contextInitialized () de estilo antiguo y desde allí usar un ELResolver para solicitar manualmente la instancia de mi bean administrado (forzando así la inicialización a suceder). Desafortunadamente, no puedo usar un ELResolver para activar la inicialización en esta etapa porque el ELResolver necesita un FacesContext y el FacesContext solo existe durante la vida útil de una solicitud.

¿Alguien conoce una forma alternativa de lograr esto?

Estoy usando MyFaces 1.2 como implementación de JSF y no puedo actualizar a 2.x en este momento.

Author: BalusC, 2010-08-30

4 answers

Mi primer pensamiento fue usar un método ServletContextListener contextInitialized () de estilo antiguo y desde allí usar un ELResolver para solicitar manualmente la instancia de mi bean administrado(forzando así la inicialización). Desafortunadamente, no puedo usar un ELResolver para activar la inicialización en esta etapa porque el ELResolver necesita un FacesContext y el FacesContext solo existe durante la vida útil de una solicitud.

No tiene que ser que complicado. Simplemente instancie el frijol y colóquelo en el ámbito de aplicación con el mismo nombre de frijol administrado como clave. JSF simplemente reutilizará el bean cuando ya esté presente en el ámbito. Con JSF encima de Servlet API, el ServletContext representa el ámbito de aplicación (como HttpSession representa el ámbito de sesión y HttpServletRequest representa el ámbito de solicitud, cada uno con los métodos setAttribute() y getAttribute()).

Esto debería hacer,

public void contextInitialized(ServletContextEvent event) {
    event.getServletContext().setAttribute("bean", new Bean());
}

Donde "bean" debe ser el mismo que el <managed-bean-name> de la aplicación scoped bean en faces-config.xml.


Solo para que conste, en JSF 2.x todo lo que necesita hacer es agregar eager=true a @ManagedBean en un frijol @ApplicationScoped.

@ManagedBean(eager=true)
@ApplicationScoped
public class Bean {
    // ...
}

Luego se creará una instancia automática al iniciar la aplicación.

O, cuando estés administrando frijoles de respaldo por CDI @Named, entonces agarra OmniFaces @Eager:

@Named
@Eager
@ApplicationScoped
public class Bean {
    // ...
}
 53
Author: BalusC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-11 09:48:35

Romain Manni-Bucau publicó una solución clara para esto que utiliza CDI 1.1 en su blog .

El truco es dejar que el bean observe la inicialización de los ámbitos del ciclo de vida integrados, es decir, ApplicationScoped en este caso. Esto también se puede utilizar para la limpieza de apagado. Así que un ejemplo se ve así:

@ApplicationScoped
public class ApplicationScopedStartupInitializedBean {
    public void init( @Observes @Initialized( ApplicationScoped.class ) Object init ) {
        // perform some initialization logic
    }

    public void destroy( @Observes @Destroyed( ApplicationScoped.class ) Object init ) {
        // perform some shutdown logic
    }
}
 8
Author: Hein Blöd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-11 09:34:10

Por lo que sé, no se puede forzar un bean administrado para ser instanciado al inicio de la aplicación.

¿Tal vez podría usar un ServletContextListener que, en lugar de crear una instancia de su bean administrado, realizará todas las operaciones de la base de datos por sí mismo?


Otra solución podría ser crear una instancia de su bean manualmente al iniciar la aplicación, y luego establecer el bean como un atributo de su ServletContext.

Aquí hay un ejemplo de código:

public class MyServletListener extends ServletContextListener {

    public void contextInitialized(ServletContextEvent sce) {
        ServletContext ctx = sce.getServletContext();
        MyManagedBean myBean = new MyManagedBean();
        ctx.setAttribute("myManagedBean", myManagedBean);
    }

}

En mi opinión, esto está lejos de código limpio, pero parece que hace el truco.

 2
Author: Vivien Barousse,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-30 13:22:04

Además de La respuesta de BalusC anterior podría usar @Startup y @Singleton (CDI), por ejemplo,

//@Named    // javax.inject.Named:       only needed for UI publishing
//@Eager    // org.omnifaces.cdi.Eager:  seems non-standard like taken @Startup below
@Startup    // javax.ejb.Startup:        like Eager, but more standard
@Singleton  // javax.ejb.Singleton:      maybe not needed if Startup is there
//@Singleton( name = "myBean" )  //      useful for providing it with a defined name
@ApplicationScoped
public class Bean {
    // ...
}

Que está muy bien explicado aquí. Funciona al menos en JPA 2.1.

 -3
Author: Andreas Dietrich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:17:05