How do I force an application-scoped bean to insta

2019-01-06 09:42发布

I can't seem to find a way to force an application-scoped managed bean to be instantiated/initialized when the web app is started. It seems that application-scoped beans get lazy-instantiated the first time the bean is accessed, not when the web app is started up. For my web app this happens when the first user opens a page in the web app for the first time.

The reason I want to avoid this is because a number of time-consuming database operations happen during the initialization of my application-scoped bean. It has to retrieve a bunch of data from persistent storage and then cache some of it that will be frequently displayed to the user in the form of ListItem elements, etc. I don't want all that to happen when the first user connects and thus cause a long delay.

My first thought was to use an old style ServletContextListener contextInitialized() method and from there use an ELResolver to manually request the instance of my managed bean (thus forcing the initialization to happen). Unfortunately, I can't use an ELResolver to trigger the initialization at this stage because the ELResolver needs a FacesContext and the FacesContext only exists during the lifespan of a request.

Does anyone know of an alternate way to accomplish this?

I am using MyFaces 1.2 as the JSF implementation and cannot upgrade to 2.x at this time.

4条回答
祖国的老花朵
2楼-- · 2019-01-06 10:06

Romain Manni-Bucau posted a neat solution to this that uses CDI 1.1 on his blog.

The trick is to let the bean observe the initialization of the built-in lifecycle scopes, i.e. ApplicationScoped in this case. This can also be used for shutdown cleanup. So an example looks like this:

@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
    }
}
查看更多
成全新的幸福
3楼-- · 2019-01-06 10:16

As far as I know, you can't force a managed bean to be instantiated at application startup.

Maybe you could use a ServletContextListener which, instead of instantiating your managed bean, will perform all the database operations itself?


Another solution might be to instantiate your bean manually at application startup, and then set the bean as an attribute of your ServletContext.

Here is a code sample:

public class MyServletListener extends ServletContextListener {

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

}

In my opinion, this is far from clean code, but it seems like it does the trick.

查看更多
你好瞎i
4楼-- · 2019-01-06 10:17

My first thought was to use an old style ServletContextListener contextInitialized() method and from there use an ELResolver to manually request the instance of my managed bean (thus forcing the initialization to happen). Unfortunately, I can't use an ELResolver to trigger the initialization at this stage because the ELResolver needs a FacesContext and the FacesContext only exists during the lifespan of a request.

It doesn't need to be that complicated. Just instantiate the bean and put it in the application scope with the same managed bean name as key. JSF will just reuse the bean when already present in the scope. With JSF on top of Servlet API, the ServletContext represents the application scope (as HttpSession represents the session scope and HttpServletRequest represents the request scope, each with setAttribute() and getAttribute() methods).

This should do,

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

where "bean" should be the same as the <managed-bean-name> of the application scoped bean in faces-config.xml.


Just for the record, on JSF 2.x all you need to do is to add eager=true to @ManagedBean on an @ApplicationScoped bean.

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

It will then be auto-instantiated at application startup.

Or, when you're managing backing beans by CDI @Named, then grab OmniFaces @Eager:

@Named
@Eager
@ApplicationScoped
public class Bean {
    // ...
}
查看更多
我只想做你的唯一
5楼-- · 2019-01-06 10:19

Additionally to BalusC's answer above you could use @Startup and @Singleton (CDI), e.g.

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

which is nicely explained here. Works in JPA 2.1 at least.

查看更多
登录 后发表回答