2010-08-30 13 views
35

Ich kann anscheinend keine Möglichkeit finden, zu erzwingen, dass eine anwendungsspezifische verwaltete Bean beim Starten der Webanwendung instanziiert/initialisiert wird. Es scheint so zu sein, dass anwendungsspezifische Beans beim ersten Zugriff auf die Bean instanziiert werden, nicht beim Start der Web-App. Bei meiner Web-App geschieht dies, wenn der erste Benutzer zum ersten Mal eine Seite in der Web-App öffnet.Wie erzwinge ich die Instanziierung eines Application-Scoped Bean beim Start der Anwendung?

Der Grund, warum ich dies vermeiden möchte, ist, dass während der Initialisierung meiner Application-Scoped Bean einige zeitaufwändige Datenbankoperationen stattfinden. Es muss eine Reihe von Daten aus dem persistenten Speicher abrufen und dann einige davon zwischenspeichern, die dem Benutzer häufig in Form von ListItem-Elementen usw. angezeigt werden. Ich möchte nicht, dass all dies geschieht, wenn der erste Benutzer eine Verbindung herstellt und somit verursachen eine lange Verzögerung.

Mein erster Gedanke war eine ServletContextListener contextInitialized() - Methode im alten Stil zu verwenden und von dort einen ELResolver zu verwenden, um manuell die Instanz meiner verwalteten Bean anzufordern (wodurch die Initialisierung erzwungen wird). Leider kann ich zu diesem Zeitpunkt keinen ELResolver verwenden, um die Initialisierung auszulösen, da der ELResolver einen FacesContext benötigt und der FacesContext nur während der Lebensdauer einer Anfrage existiert.

Kennt jemand eine alternative Möglichkeit, dies zu erreichen?

Ich verwende MyFaces 1.2 als JSF-Implementierung und kann derzeit nicht auf 2.x aktualisieren.

Antwort

49

Mein erster Gedanke war, einen alten Stil ServletContextListener contextInitialized() -Methode verwendet wird und von dort eine ELResolver verwenden, um manuell anfordern, die Instanz von meinem Managed Bean (also die Initialisierung geschehen zwingen). Leider kann ich zu diesem Zeitpunkt keinen ELResolver verwenden, um die Initialisierung auszulösen, da der ELResolver einen FacesContext benötigt und der FacesContext nur während der Lebensdauer einer Anfrage existiert.

Es braucht nicht dass kompliziert zu sein. Einfach die Bean instanziieren und sie in den Anwendungsbereich mit dem gleichen verwalteten Bean-Namen als Schlüssel einfügen. JSF wird nur die die Bohne wiederverwenden, wenn sie bereits im Umfang vorhanden ist. Mit JSF über der Servlet-API stellt ServletContext den Anwendungsbereich der Anwendung dar (da HttpSession den Sitzungsumfang darstellt und HttpServletRequest den Anforderungsbereich darstellt, jeweils mit den Methoden setAttribute() und getAttribute()).

Dies tun soll,

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

wo "bean" das gleiche sein soll wie die <managed-bean-name> der Anmeldung bean scoped in faces-config.xml.


Nur für das Protokoll, auf JSF 2.x alles, was Sie tun müssen, ist eager=true zu @ManagedBean auf einer @ApplicationScoped Bohne hinzuzufügen.

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

Sie wird beim Start der Anwendung automatisch instanziiert.

Oder, wenn Sie die Sicherung Bohnen sind die Verwaltung von CDI @Named, dann greifen OmniFaces@Eager:

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

+1 für eine effektive Lösung. Eine kleine Frage: Ist es offiziell ok, dies gemäß der Spezifikation zu tun, oder hängt es von einigen JSF-Implementierungsdetails ab? Ich meine, eine JSF-Implementierung könnte entscheiden, ob eine Anwendungs-Bean in einer völlig nicht offensichtlichen Weise instanziiert wird und dann zum Beispiel die Bean neu erstellen würde. – ewernli

+0

@BalusC Das war so einfach und es funktioniert. Ich hatte vermieden, die setAttribute() -Methode im ServletContext zu verwenden, weil ich dachte, dass es mit JSF interferieren würde, aber anscheinend nicht. PS: Liebe deine Seite bei blogspot.com - dein alter Artikel über die Verwendung von DataTables war hilfreich. –

+0

@Jim: Gern geschehen. @ewernli: Die Spezifikation erlaubt dies nicht explizit, aber sie lässt das auch nicht explizit zu. Die Spezifikation beschreibt jedoch, dass eine verwaltete Bean erstellt werden muss, wenn sie nicht im Bereich vorhanden ist. – BalusC

1

Soweit ich weiß, können Sie nicht erzwingen, dass eine verwaltete Bean beim Start der Anwendung instanziiert wird.

Vielleicht könnten Sie einen ServletContextListener verwenden, der alle Datenbankoperationen selbst durchführt, statt Ihre verwalteten Bean zu instanziieren?


Eine andere Lösung könnte sein, Ihre Bohne manuell beim Start der Anwendung zu instanziiert, und legen Sie dann die Bohne als ein Attribut Ihres ServletContext.

Hier ist ein Codebeispiel:

public class MyServletListener extends ServletContextListener { 

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

} 

Meiner Meinung nach ist dies weit von sauberen Code ist, aber es scheint, wie es funktioniert der Trick.

7

Romain Manni-Bucau veröffentlicht eine saubere Lösung dieses Problems, die CDI 1.1 auf seinem blog verwendet.

Der Trick besteht darin, die Bean die Initialisierung der integrierten Lifecycle-Bereiche beobachten zu lassen, d. H. ApplicationScoped in diesem Fall. Dies kann auch für die Bereinigung des Herunterfahrens verwendet werden. So sieht ein Beispiel wie folgt aus:

@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

Bei der Migration von GlassFish 4.1 zu Payara 4.1.1.164, stieß ich auf seltsame Fehler, bei denen ein '@ PersistenceContext'-Feld in einer' @ ApplicationScoped'-Bean, die dieses Muster für die Initialisierung verwendet, nicht korrekt injiziert wurde. Es gab Fehler wie diese: "Keine gültige EE-Umgebung für die Injektion von ApplicationScopedStartupInitializedBean". Stellt sich heraus, dass der Parameter vom Typ 'ServletContext' sein muss, um das zu beheben, d. H.' Public void init (@Observes @Initialized (ApplicationScoped.class) ServletContext init) ' –

+0

Ich bin dabei, dies als Fehler zu speichern. Haben Sie jemals einen Fehlerbericht darüber gefunden oder haben Sie ihn selbst eingereicht? Es gab https://github.com/payara/Payara/issues/299, was entweder etwas anderes ist oder nicht ausreichend war. –

+0

@KarlRichter Nein, das habe ich nicht weiter untersucht. Ich dachte, die Änderung des Parametertyps könnte durch eine strengere Anwendung einer Spezifikation oder etwas Ähnlichem erzwungen worden sein. –

-2

Zusätzlich zu BalusC's answer above könnten Sie @Startup und @Singleton (CDI), z.B.

//@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 { 
    // ... 
} 

das ist schön erklärt here. Funktioniert in JPA 2.1 mindestens.

+2

Das ist ein EJB nicht eine gemanagte Bean, die ganz anders ist. EJBs laufen im Backend und verwalteten Beans im Frontend. EJBs werden auch im Transaktionskontext ausgeführt. Die Aussage "arbeitet in JPA" ist seltsam. EJBs benötigen keine JPA, um ausgeführt zu werden. – BalusC

+0

@BalusC Ich denke, Sie sind nicht ganz richtig und übertreiben die Bedeutung/Verwendung und es sieht fraglich für mich aus. https://en.wikipedia.org/wiki/Enterprise_JavaBeans. * funktioniert in JPA * sollte bedeuten, dass ich es (nur) in einer Umgebung getestet habe, die auf JPA 2.1 * basiert. In vielen realen Szenarios/Setups würde ich sagen, dass es schwer ist, aus einer abstrakten/modellierenden Perspektive zu sagen, ob etwas eine EJB- oder eine anwendungsspezifische verwaltete Bean ist. Es wäre also interessant zu wissen, warum es schlecht ist, das zu tun, was ich vorgeschlagen habe, obwohl es für mich technisch und modellmäßig funktioniert. –

+1

Es ist nicht in sich selbst schlecht, im Gegenteil. Es ist nur so, dass Sie die Frage in ihrer aktuellen Form überhaupt nicht beantworten. Sie haben gerade Enterprise-Beans mit verwalteten Beans verwechselt, worauf ich gerade hingewiesen habe. – BalusC