Как принудительно использовать приложение 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.