如何强制应用程序范围的bean在应用程序启动时实例化?

我似乎无法find一种方法来强制应用程序范围的托pipebean在Web应用程序启动时被实例化或初始化。 看起来应用程序范围的bean在第一次访问bean的时候得到了延迟实例化,而不是在web应用程序启动的时候。 对于我的networking应用程序,当第一个用户第一次在Web应用程序中打开一个页面时会发生这种情况。

我想避免这种情况的原因是因为在我的应用程序范围的bean的初始化过程中发生了一些耗时的数据库操作。 它必须从持久性存储中检索一堆数据,然后caching一些以ListItem元素等forms经常显示给用户的数据。我不希望第一个用户连接时发生这种情况,因此造成很长的延误。

我的第一个想法是使用旧式ServletContextListener contextInitialized()方法,并从那里使用ELResolver手动请求我的托pipebean的实例(从而强制初始化发生)。 不幸的是,我不能在这个阶段使用ELResolver来触发初始化,因为ELResolver需要一个FacesContext,而FacesContext只在请求的生命周期中存在。

有谁知道一个替代方法来完成这个?

我正在使用MyFaces 1.2作为JSF实现,此时无法升级到2.x。

我的第一个想法是使用旧式ServletContextListener contextInitialized()方法,并从那里使用ELResolver手动请求我的托pipebean的实例(从而强制初始化发生)。 不幸的是,我不能在这个阶段使用ELResolver来触发初始化,因为ELResolver需要一个FacesContext,而FacesContext只在请求的生命周期中存在。

它不需要那么复杂。 只要实例化这个bean,并把它放在应用程序的作用域中,并且使用 key 相同的托pipebean名称。 JSF将在已经存在的范围内重用这个bean。 使用Servlet API之上的JSF, ServletContext表示应用程序范围(因为HttpSession表示会话范围, HttpServletRequest表示请求范围,每个范围都有setAttribute()getAttribute()方法)。

这应该做,

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

其中"bean"应该与faces-config.xml应用程序作用域bean的<managed-bean-name>相同。


为了logging,在JSF 2.x中,您只需将eager=true添加到@ApplicationScoped bean上的@ManagedBean

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

它将在应用程序启动时自动实例化。

或者,当您通过CDI @Namedpipe理后台Bean时,则抓取OmniFaces @Eager

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

Romain Manni-Bucau在他的博客上发布了一个使用CDI 1.1的完整解决scheme。

诀窍是让bean观察内置生命周期范围的初始化,即在这种情况下ApplicationScoped 。 这也可以用于closures清理。 所以一个例子看起来像这样:

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

据我所知,你不能强迫一个托pipe的bean在应用程序启动时被实例化。

也许你可以使用一个ServletContextListener,而不是实例化你的托pipebean,将自己执行所有的数据库操作?


另一个解决scheme可能是在应用程序启动时手动实例化你的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); } } 

在我看来,这远不是干净的代码,但它似乎是一个窍门。

除了BalusC的回答上面,你可以使用@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 { // ... } 

这在这里很好地解释。 至less在JPA 2.1中工作。