2010-08-30 33 views
35

我似乎無法找到強制應用程序範圍的託管bean在Web應用程序啓動時實例化/初始化的方法。看起來應用程序範圍的bean在第一次訪問bean的時候得到了延遲實例化,而不是在Web應用程序啓動的時候。對於我的Web應用程序,第一個用戶第一次在Web應用程序中打開一個頁面時會發生這種情況。如何強制應用程序範圍的bean在應用程序啓動時實例化?

我想避免這種情況的原因是因爲在初始化我的應用程序範圍的bean期間發生了大量耗時的數據庫操作。它必須從持久存儲中檢索一堆數據,然後緩存一些以ListItem元素等形式經常顯示給用戶的數據。我不想在第一個用戶連接時發生這種情況,因此導致很長的延遲。

我的第一個想法是使用舊式的ServletContextListener contextInitialized()方法,並從那裏使用ELResolver手動請求託管bean的實例(從而強制執行初始化)。不幸的是,我不能在這個階段使用ELResolver觸發初始化,因爲ELResolver需要一個FacesContext,並且FacesContext僅在請求的生命週期中存在。

有誰知道一個替代方法來完成這個?

我使用MyFaces 1.2作爲JSF實現,此時無法升級到2.x。

回答

49

我首先想到的是使用舊式的ServletContextListener contextInitialized()方法,並從那裏使用ELResolver手動請求我的管理bean的實例(從而迫使初始化發生)。不幸的是,我不能在這個階段使用ELResolver觸發初始化,因爲ELResolver需要一個FacesContext,並且FacesContext僅在請求的生命週期中存在。

它並不需要是複雜。只需實例化bean,並將其與相同的託管bean名稱作爲關鍵字放入應用程序範圍中。 JSF將只在重用該bean已經存在的範圍內。使用Servlet API之上的JSF,ServletContext代表應用範圍(因爲HttpSession代表會話範圍,而HttpServletRequest代表請求範圍,每個都有setAttribute()getAttribute()方法)。

這應該做的,

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

其中"bean"應該是相同的應用程序的<managed-bean-name>faces-config.xml作用域bean。


只是爲了記錄在案,對JSF 2.x的所有你需要做的是添加eager=true@ManagedBean@ApplicationScoped豆。

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

然後它會在應用程序啓動時自動實例化。

或者,當你被CDI @Named管理支持豆,然後抓住OmniFaces@Eager

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

+1有效的解決方案。一個小問題:根據規範做這件事是正式的,還是依賴於一些JSF實現細節?我的意思是,JSF實現可以決定跟蹤應用程序bean是否以完全不明顯的方式實例化,然後重新創建bean。 – ewernli 2010-08-30 13:40:50

+0

@BalusC這是如此簡單,它的工作原理。我曾經避免在ServletContext中使用setAttribute()方法,因爲我認爲它會干擾JSF,但顯然不是。 PS:在blogspot.com上愛你的頁面 - 關於使用DataTables的舊文章很有幫助。 – 2010-08-30 13:45:11

+0

@Jim:不客氣。 @ewernli:規範沒有明確允許,但它也沒有明確禁止。然而,規範描述瞭如果託管bean不在範圍中,則必須創建託管bean。 – BalusC 2010-08-30 14:02:02

1

據我所知,你不能強制在應用程序啓動時實例化託管bean。

也許你可以使用一個ServletContextListener,而不是實例化你的託管bean,它將自己執行所有的數據庫操作?


另一種解決方案可能是在應用程序啓動時手動實例化您的bean,然後將bean設置爲您的ServletContext的屬性。

下面是一個代碼示例:

public class MyServletListener extends ServletContextListener { 

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

} 

在我看來,這還遠遠沒有乾淨的代碼,但它似乎像它的伎倆。

7

羅曼曼尼 - Bucau貼很好地解決了這一點,在他的blog使用CDI 1.1。

訣竅是讓bean觀察內置生命週期範圍的初始化,即在這種情況下爲ApplicationScoped。這也可以用於關閉清理。因此,一個例子是這樣的:

@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

從GlassFish 4.1遷移到Payara 4.1.1時。164,我遇到了一個奇怪的錯誤,那就是在使用這個模式進行初始化初始化的@ ApplicationScoped bean中的@PersistenceContext字段沒有被正確注入。有這樣的錯誤:「沒有有效的EE環境注入ApplicationScopedStartupInitializedBean」。原來這個參數的類型必須是'ServletContext'來解決這個問題,也就是'public void init(@Observes @Initialized(ApplicationScoped.class)ServletContext init)' – 2017-01-31 15:45:12

+0

我正要把這個文件作爲一個bug。你有沒有找到關於這個問題的錯誤報告或你自己提交了嗎?有https://github.com/payara/Payara/issues/299這或者是關於別的東西或者是不夠的。 – 2018-01-16 20:19:57

+0

@KarlRichter不,我沒有進一步調查。我認爲參數類型的改變可能是通過更嚴格的應用某些規範或類似的東西來實施的。 – 2018-01-17 07:28:18

-2

此外,以BalusC's answer above你可以使用@Startup@Singleton(CDI),例如

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

這是很好解釋here。 至少在JPA 2.1中有效。

+2

這是一個EJB而不是一個託管bean,這是完全不同的。 EJB在後端運行,在前端運行受管bean。 EJB也在事務上下文中運行。該聲明「在JPA中工作」很奇怪。 EJB根本不需要運行JPA。 – BalusC 2016-01-19 08:48:56

+0

@BalusC我認爲你不太正確,誇大其意義/用法,這對我來說似乎有爭議。 https://en.wikipedia.org/wiki/Enterprise_JavaBeans。 *在JPA *中工作應該意味着我*在僅基於JPA 2.1 *的環境中對其進行了測試。在很多現實生活場景/設置中,我會說,從抽象/建模的角度來看,很難明確地說 - 無論是EJB還是應用程序範圍的管理bean。所以,知道爲什麼做到我所建議的做法很糟糕,儘管它在技術上和從建模的角度來看對我來說都很有用。 – 2016-01-19 13:08:33

+1

它本身並不壞,恰恰相反。只是你在技術上根本不回答目前的問題。你只是將企業bean與託管bean混淆了,我只是指出了這一點。 – BalusC 2016-01-19 13:13:35

相關問題