2010-08-30 8 views
35

Parece que no puedo encontrar una manera de forzar que un bean administrado con ámbito de aplicación se cree una instancia/inicialice cuando se inicia la aplicación web. Parece que los beans con ámbito de aplicación obtienen instantáneas perezosas la primera vez que se accede al bean, 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.¿Cómo fuerzo a un bean con ámbito de aplicación para crear instancias al inicio de la aplicación?

La razón por la que deseo evitar esto es porque suceden varias operaciones de base de datos que consumen mucho tiempo durante la inicialización de mi bean con ámbito de aplicación. Tiene que recuperar un montón de datos del almacenamiento persistente y luego almacenar en caché algunos de los 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 conecta y por lo tanto causa una larga demora

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

¿Alguien sabe de una manera alternativa de lograr esto?

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

Respuesta

49

Lo primero que pensé fue utilizar un viejo método ServletContextListener contextInitialized() estilo y desde allí use un ELResolver para solicitar manualmente la instancia de mi bean gestionado (lo que obliga a la inicialización a suceder). Lamentablemente, no puedo usar un ELResolver para activar la inicialización en esta etapa porque ELResolver necesita un FacesContext y el FacesContext solo existe durante la vigencia de una solicitud.

No tiene por qué ser que complicado. Simplemente crea una instancia del bean y colócalo en el ámbito de aplicación con el mismo nombre de bean administrado como clave. JSF solo reutilizará el bean cuando ya esté presente en el alcance. Con JSF encima de Servlet API, el ServletContext representa el ámbito de la aplicación (como HttpSession representa el alcance de la sesión y HttpServletRequest representa el ámbito de la solicitud, cada uno con setAttribute() y getAttribute() métodos).

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 en el ámbito de frijol en faces-config.xml.


Sólo para que conste, en 2.x JSF todo lo que necesita hacer es añadir a eager=true@ManagedBean en un bean @ApplicationScoped.

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

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

O, cuando administre beans de respaldo por el CDI @Named, a continuación, coger OmniFaces@Eager:

@Named 
@Eager 
@ApplicationScoped 
public class Bean { 
    // ... 
} 
+0

+1 para una solución efectiva. Una pequeña pregunta: ¿está oficialmente bien hacer eso según la especificación, o se basa en algunos detalles de implementación de JSF? Quiero decir, una implementación de JSF podría decidir realizar un seguimiento de si una instancia de la aplicación fue instanciada de una manera completamente no obvia y luego recrearía el bean, por ejemplo. – ewernli

+0

@BalusC Eso fue muy simple y funciona. Había evitado utilizar el método setAttribute() en ServletContext porque pensé que interferiría con JSF, pero aparentemente no. PD: Me encanta tu página en blogspot.com: tu antiguo artículo sobre el uso de DataTables fue útil. –

+0

@Jim: de nada. @ewernli: la especificación no permite explícitamente eso, pero tampoco explícitamente no permite eso. Sin embargo, la especificación describe que un bean administrado debe crearse cuando no está presente en el alcance. – BalusC

1

Hasta donde yo sé, no se puede obligar a un bean administrado a ser instanciado al inicio de la aplicación.

Tal vez podría usar un ServletContextListener que, en lugar de crear instancias de su bean administrado, realizará todas las operaciones de la base de datos.


Otra solución podría ser la de crear una instancia de un bean de forma manual en el inicio de la aplicación, y luego ponga el frijol como un atributo de su ServletContext.

Aquí es 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 un código limpio, pero parece que lo hace el truco.

7

Romain Manni-Bucau registró una clara solución a 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 integrado, es decir, ApplicationScoped en este caso. Esto también se puede usar para la limpieza del cierre. Así que un ejemplo es el siguiente:

@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 
    } 
} 
+0

Al migrar de GlassFish 4.1 a Payara 4.1.1.164, encontré errores extraños en los que un campo '@ PersistenceContext' en un bean' @ ApplicationScoped' que utiliza este patrón para la inicialización ansiosa no se inyectó correctamente. Hubo errores como este: "No hay un entorno EE válido para la inyección de ApplicationScopedStartupInitializedBean". Resulta que el parámetro debe ser de tipo 'ServletContext' para corregir esto, es decir' public void init (@Observes @Initialized (ApplicationScoped.class) ServletContext init) ' –

+0

Estoy a punto de presentar esto como un error. ¿Alguna vez encontró un informe de error sobre esto o lo ha archivado usted mismo? Hubo https://github.com/payara/Payara/issues/299 que, o bien se trata de otra cosa o no fue suficiente. –

+0

@KarlRichter No, no investigué esto más. Pensé que el cambio del tipo de parámetro podría haber sido impuesto por una aplicación más estricta de alguna especificación o algo así. –

-2

Adicionalmente a BalusC's answer above podría utilizar @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 explicada here. Funciona en JPA 2.1 al menos.

+2

Eso es un EJB no un frijol administrado, que es bastante diferente. Los EJB se ejecutan en el back-end y los beans gestionados en el frontend. Los EJB también se ejecutan en contexto transaccional. La afirmación "funciona en JPA" es extraña. Los EJB no requieren JPA en absoluto para ejecutarse. – BalusC

+0

@BalusC Creo que no eres del todo correcto y exageras el significado/uso y parece discutible para mí. https://en.wikipedia.org/wiki/Enterprise_JavaBeans. * funciona en JPA * debe significar que * lo probé (solo) en un entorno basado en JPA 2.1 *. En muchos escenarios/configuraciones de la vida real, diría que es difícil decir, desde un punto de vista abstracto/de modelado, si algo es un EJB o un bean gestionado con un ámbito de aplicación. Por lo tanto, sería interesante saber por qué es malo hacer lo que sugerí, aunque me sirve técnicamente y desde un punto de vista de modelado. –

+1

No está mal en sí mismo, al contrario. Es solo que técnicamente no estás respondiendo la pregunta en su forma actual. Acabas de confundir los enterprise beans con beans administrados y yo solo lo señalé. – BalusC

Cuestiones relacionadas