web-dev-qa-db-fra.com

Comment forcer un bean à portée d'application à instancier au démarrage de l'application?

Je n'arrive pas à trouver un moyen de forcer un bean géré à portée d'application à instancier/initialiser au démarrage de l'application Web. Il semble que les beans étendus à l'application soient instanciés paresseux lors du premier accès au bean, et non au démarrage de l'application Web. Pour mon application Web, cela se produit lorsque le premier utilisateur ouvre une page dans l'application Web pour la première fois.

La raison pour laquelle je veux éviter cela est qu'un certain nombre d'opérations de base de données chronophages se produisent pendant l'initialisation de mon bean à portée d'application. Il doit récupérer un tas de données du stockage persistant, puis mettre en cache certaines d'entre elles qui seront fréquemment affichées pour l'utilisateur sous la forme d'éléments ListItem, etc. Je ne veux pas que tout cela se produise lorsque le premier utilisateur se connecte et donc causer un long retard.

Ma première pensée a été d'utiliser une méthode contextInitialized () ServletContextListener de style ancien et à partir de là, utiliser un ELResolver pour demander manuellement l'instance de mon bean géré (forçant ainsi l'initialisation à se produire). Malheureusement, je ne peux pas utiliser un ELResolver pour déclencher l'initialisation à ce stade car l'ELResolver a besoin d'un FacesContext et le FacesContext n'existe que pendant la durée de vie d'une demande.

Quelqu'un connaît-il une autre façon d'accomplir cela?

J'utilise MyFaces 1.2 comme implémentation JSF et je ne peux pas passer à 2.x pour le moment.

38
Jim Tough

Ma première pensée a été d'utiliser une méthode contextInitialized () ServletContextListener de style ancien et à partir de là, utiliser un ELResolver pour demander manuellement l'instance de mon bean géré (forçant ainsi l'initialisation à se produire). Malheureusement, je ne peux pas utiliser un ELResolver pour déclencher l'initialisation à ce stade car l'ELResolver a besoin d'un FacesContext et le FacesContext n'existe que pendant la durée de vie d'une demande.

Il n'est pas nécessaire que ce soit ça compliqué. Il suffit d'instancier le bean et de le placer dans la portée de l'application avec le nom de bean géré same comme clé. JSF va juste réutiliser le bean lorsqu'il est déjà présent dans la portée. Avec JSF au-dessus de l'API Servlet, ServletContext représente la portée de l'application (comme HttpSession représente la portée de la session et HttpServletRequest représente la portée de la demande, chacun avec setAttribute() et getAttribute()).

Cela devrait faire,

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

"bean" doit être identique à <managed-bean-name> du bean de portée d'application dans faces-config.xml.


Pour mémoire, sur JSF 2.x, tout ce que vous avez à faire est d'ajouter eager=true à @ManagedBean sur un @ApplicationScoped haricot.

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

Il sera ensuite instancié automatiquement au démarrage de l'application.

Ou, lorsque vous gérez des beans de sauvegarde par CDI @Named, puis saisissez OmniFaces@Eager :

@Named
@Eager
@ApplicationScoped
public class Bean {
    // ...
}
58
BalusC

Romain Manni-Bucau a posté une solution intéressante qui utilise CDI 1.1 sur son blog .

L'astuce consiste à laisser le bean observer l'initialisation des portées de cycle de vie intégrées, c'est-à-dire ApplicationScoped dans ce cas. Cela peut également être utilisé pour le nettoyage d'arrêt. Donc, un exemple ressemble à ceci:

@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
    }
}
16
Hein Blöd

Pour autant que je sache, vous ne pouvez pas forcer l'instanciation d'un bean géré au démarrage de l'application.

Peut-être pourriez-vous utiliser un ServletContextListener qui, au lieu d'instancier votre bean géré, effectuera lui-même toutes les opérations de la base de données?


Une autre solution pourrait être d'instancier votre bean manuellement au démarrage de l'application, puis de définir le bean comme attribut de votre ServletContext.

Voici un exemple de code:

public class MyServletListener extends ServletContextListener {

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

}

À mon avis, c'est loin d'être du code propre, mais il semble que cela fasse l'affaire.

2
Vivien Barousse