2010-08-30 1 views
35

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

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

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

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

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

ответ

49

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

Он не должен быть что сложно. Просто создайте экземпляр компонента и поместите его в область приложения с тем же управляемым bean-именем в качестве ключа. JSF будет только повторно использовать bean, когда уже присутствует в области. С JSF поверх API Servlet 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 { 
    // ... 
} 

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

Или, когда вы управляете отступающими бобами по КДИ @Named, а затем захватить OmniFaces@Eager:

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

+1 для эффективного решения. Один маленький вопрос: официально ли это делать в соответствии со спецификацией или он полагается на некоторые детали реализации JSF? Я имею в виду, что реализация JSF могла бы решить отслеживать, был ли экземпляр компонента приложения полностью невидимым, и затем воссоздает компонент, например. – ewernli

+0

@BalusC Это было так просто и работает. Я избегал использования метода setAttribute() в ServletContext, потому что я думал, что это будет мешать JSF, но, видимо, нет. PS: Любите свою страницу на blogspot.com - ваша старая статья об использовании DataTables была полезна. –

+0

@ Jim: добро пожаловать. @ewernli: спецификация явно не допускает этого, но это также не объясняет это явно. Спецификация, однако, описывает, что управляемый компонент должен быть создан, когда он не присутствует в области. – BalusC

1

Насколько я знаю, вы не можете заставить управляемый компонент быть создан при запуске приложения.

Возможно, вы могли бы использовать ServletContextListener, который вместо создания управляемых компонентов будет выполнять все операции с базой данных?


Другим решением может быть создать экземпляр бина вручную при запуске приложения, а затем установить компонент в качестве атрибута вашей ServletContext.

Вот пример кода:

public class MyServletListener extends ServletContextListener { 

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

} 

На мой взгляд, это далеко от чистого кода, но кажется, что это делает трюк.

7

Romain Маннь-Bucau опубликовал изящное решение для этого, который использует CDI 1.1 на его blog.

Фокус в том, чтобы позволить компоненту наблюдать инициализацию встроенных областей жизненного цикла, то есть 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 
    } 
} 
+0

При переносе с GlassFish 4.1 на Payara 4.1.1.164, я столкнулся с странными ошибками, в которых поле '@ PersistenceContext' в компоненте' @ ApplicationScoped', которое использует этот шаблон для инициативной инициализации, не было правильно введено. Появились такие ошибки: «Нет допустимой среды EE для инъекции ApplicationScopedStartupInitializedBean». Выключает параметр должен быть типа 'ServletContext', чтобы исправить это, т. Е.' Public void init (@Observes @Initialized (ApplicationScoped.class) ServletContext init) ' –

+0

Я собираюсь записать это как ошибку. Вы когда-нибудь находили сообщение об ошибке или вы сами его подавали? Был https://github.com/payara/Payara/issues/299, который либо о чем-то другом, либо был недостаточным. –

+0

@KarlRichter Нет, я не исследовал это дальше. Я думал, что изменение типа параметра могло быть усилено более строгим применением какой-либо спецификации или что-то в этом роде. –

-2

Дополнительно к BalusC's answer above можно использовать @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 { 
    // ... 
} 

который хорошо пояснен here. Работы в JPA 2.1 как минимум.

+2

Это EJB не управляемый боб, который совсем другой. EJB запускаются в backend и управляемых компонентах в frontend. EJB также работают в транзакционном контексте. Утверждение «работает в JPA» странно. EJB не требуют выполнения JPA вообще. – BalusC

+0

@BalusC Я думаю, что вы не совсем правы и преувеличиваете смысл/использование, и это выглядит спорным для меня. https://en.wikipedia.org/wiki/Enterprise_JavaBeans. * работает в JPA * должен означать, что я * тестировал его (только) в среде на основе JPA 2.1 *. Во многих реальных сценариях/настройках я бы сказал, что трудно сказать - с абстрактной/моделирующей точки зрения - является ли что-то EJB или управляемым компонентом с областью приложения. Поэтому было бы интересно узнать, почему плохо делать то, что я предлагал, хотя оно работает для меня технически и с точки зрения моделирования. –

+1

Это не плохо, в свою очередь, наоборот. Просто вы технически не отвечаете на вопрос в своей нынешней форме. Вы просто путали заводские бобы с управляемыми бобами, и я просто это заметил. – BalusC

Смежные вопросы