Как принудительно использовать приложение bean для приложения при запуске приложения?

Я не могу найти способ заставить управляемый приложением bean создать/инициализировать при запуске веб-приложения. Кажется, что приложение beans с областью приложения получает lazy-instantiated при первом доступе bean, а не при запуске веб-приложения. Для моего веб-приложения это происходит, когда первый пользователь впервые открывает страницу в веб-приложении.

Причина, по которой я хочу избежать этого, заключается в том, что во время инициализации моего приложения bean происходит множество трудоемких операций с базой данных. Он должен извлекать кучу данных из постоянного хранилища, а затем кэшировать некоторые из них, которые будут часто отображаться пользователю в виде элементов ListItem и т.д. Я не хочу, чтобы все это происходило, когда первый пользователь подключается и, следовательно, вызывают длительную задержку.

Моя первая мысль заключалась в использовании старого метода ServletContextListener contextInitialized(), и оттуда используйте ELResolver, чтобы вручную запросить экземпляр моего управляемого bean (тем самым заставив инициализацию произойти). К сожалению, я не могу использовать ELResolver для запуска инициализации на этом этапе, потому что ELResolver нуждается в FacesContext, а FacesContext существует только в течение срока службы запроса.

Кто-нибудь знает о альтернативном способе выполнения этого?

Я использую MyFaces 1.2 в качестве реализации JSF и не могу обновить до 2.x в настоящее время.

Ответы

Ответ 1

Моя первая мысль заключалась в использовании старого метода ServletContextListener contextInitialized(), и оттуда используйте ELResolver, чтобы вручную запросить экземпляр моего управляемого bean (тем самым заставив инициализацию произойти). К сожалению, я не могу использовать ELResolver для запуска инициализации на этом этапе, потому что ELResolver нуждается в FacesContext, а FacesContext существует только в течение срока службы запроса.

Это не должно быть так сложно. Просто создайте экземпляр bean и поместите его в область приложения с помощью того же управляемого bean имени в качестве ключа. JSF будет повторно использовать bean, когда он уже присутствует в области. С JSF поверх API сервлета ServletContext представляет область приложения (поскольку HttpSession представляет область сеанса, а HttpServletRequest представляет область запроса, каждая из которых имеет методы setAttribute() и getAttribute()).

Это должно сделать,

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

где "bean" должно быть таким же, как <managed-bean-name> приложения с областью bean в faces-config.xml.


Только для записи на JSF 2.x вам нужно добавить eager=true в @ManagedBean на @ApplicationScoped bean.

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

Затем он будет автоматически создан при запуске приложения.

Или, когда вы управляете поддержкой beans с помощью CDI @Named, затем возьмите OmniFaces @Eager:

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

Ответ 2

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

Ответ 3

Насколько я знаю, вы не можете принудительно создать управляемый 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);
    }

}

По-моему, это далеко не чистый код, но похоже, что это трюк.

Ответ 4

В дополнение к ответу 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.