40

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

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

我的第一个想法是使用旧式 ServletContextListener contextInitialized() 方法,然后使用 ELResolver 手动请求托管 bean 的实例(从而强制进行初始化)。不幸的是,我不能在这个阶段使用 ELResolver 来触发初始化,因为 ELResolver 需要 FacesContext 并且 FacesContext 仅在请求的生命周期内存在。

有谁知道实现此目的的替代方法?

我使用 MyFaces 1.2 作为 JSF 实现,目前无法升级到 2.x。

4

4 回答 4

58

我的第一个想法是使用旧式 ServletContextListener contextInitialized() 方法,然后使用 ELResolver 手动请求托管 bean 的实例(从而强制进行初始化)。不幸的是,我不能在这个阶段使用 ELResolver 来触发初始化,因为 ELResolver 需要 FacesContext 并且 FacesContext 仅在请求的生命周期内存在。

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

这应该做,

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

where"bean"应该<managed-bean-name>faces-config.xml.


只是为了记录,在 JSF 2.x 上,您需要做的就是添加eager=true@ManagedBean一个@ApplicationScopedbean 上。

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

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

或者,当您通过 CDI 管理支持 bean 时,请使用@NamedOmniFaces : @Eager

@Named
@Eager
@ApplicationScoped
public class Bean {
    // ...
}
于 2010-08-30T13:24:04.223 回答
24

Romain Manni-Bucau 在他的博客上发布了一个使用 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
    }
}
于 2015-09-11T09:34:10.360 回答
2

据我所知,您不能强制在应用程序启动时实例化托管 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);
    }

}

在我看来,这远非干净的代码,但它似乎可以解决问题。

于 2010-08-30T13:06:01.490 回答
-1

除了上面 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 {
    // ...
}

在这里很好地解释了这一点。至少在 JPA 2.1 中工作。

于 2016-01-19T08:30:02.787 回答