Как заставить bean-объект в области приложения создавать экземпляры при запуске приложения?
Кажется, я не могу найти способ принудительно создать экземпляр / инициализированный управляемый компонент приложения при запуске веб-приложения. Кажется, что bean-объекты области приложения ленивые создаются при первом обращении к bean-компоненту, а не при запуске веб-приложения. Для моего веб-приложения это происходит, когда первый пользователь впервые открывает страницу в веб-приложении.
Причина, по которой я хочу этого избежать, заключается в том, что во время инициализации моего bean-объекта в области приложения происходит ряд трудоемких операций с базой данных. Он должен извлечь кучу данных из постоянного хранилища, а затем кэшировать некоторые из них, которые будут часто отображаться пользователю в форме элементов ListItem и т. Д. Я не хочу, чтобы все это происходило, когда первый пользователь подключается и, таким образом, вызвать долгую задержку.
Моей первой мыслью было использовать метод contextInitialized() ServletContextListener старого стиля и оттуда использовать ELResolver для ручного запроса экземпляра моего управляемого компонента (таким образом, вызывая инициализацию). К сожалению, я не могу использовать ELResolver для запуска инициализации на этом этапе, потому что для ELResolver требуется FacesContext, а FacesContext существует только в течение срока службы запроса.
Кто-нибудь знает альтернативный способ сделать это?
Я использую MyFaces 1.2 в качестве реализации JSF и не могу перейти на 2.x в настоящее время.
4 ответа
Моей первой мыслью было использовать метод contextInitialized() ServletContextListener старого стиля и оттуда использовать ELResolver для ручного запроса экземпляра моего управляемого компонента (таким образом, вызывая инициализацию). К сожалению, я не могу использовать ELResolver для запуска инициализации на этом этапе, потому что для ELResolver требуется FacesContext, а FacesContext существует только в течение срока службы запроса.
Это не должно быть так сложно. Просто создайте экземпляр компонента и поместите его в область приложения с тем же именем управляемого компонента, что и ключ. JSF просто повторно использует бин, когда он уже присутствует в области видимости. С JSF поверх Servlet API, ServletContext
представляет область применения (как HttpSession
представляет область сеанса и HttpServletRequest
представляет объем запроса, каждый с setAttribute()
а также getAttribute()
методы).
Это должно сделать,
public void contextInitialized(ServletContextEvent event) {
event.getServletContext().setAttribute("bean", new Bean());
}
где "bean"
должен быть таким же, как <managed-bean-name>
бин области действия приложения в faces-config.xml
,
Просто для записи, на JSF 2.x все, что вам нужно сделать, это добавить eager=true
в @ManagedBean
на @ApplicationScoped
боб.
@ManagedBean(eager=true)
@ApplicationScoped
public class Bean {
// ...
}
Затем он будет автоматически создан при запуске приложения.
Или когда вы управляете поддержкой бобов CDI @Named
, а затем захватить OmniFaces @Eager
:
@Named
@Eager
@ApplicationScoped
public class Bean {
// ...
}
Ромен Манни-Букау опубликовал в своем блоге аккуратное решение, которое использует CDI 1.1.
Хитрость заключается в том, чтобы позволить бину наблюдать инициализацию встроенных областей жизненного цикла, т.е. 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
}
}
Насколько я знаю, вы не можете принудительно создать экземпляр управляемого компонента при запуске приложения.
Может быть, вы могли бы использовать ServletContextListener, который вместо создания экземпляра вашего управляемого компонента будет сам выполнять все операции с базой данных?
Другим решением может быть создание экземпляра вашего компонента вручную при запуске приложения, а затем установка компонента в качестве атрибута вашего 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 {
// ...
}
что хорошо объяснено здесь. Работает в JPA 2.1 как минимум.