Как заставить 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 как минимум.

Другие вопросы по тегам