如何强制应用程序范围的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 @Named
pipe理后台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中工作。