【问题标题】:JSF initialize application-scope bean when context initializedJSF 在上下文初始化时初始化应用程序范围的 bean
【发布时间】:2010-09-23 19:48:00
【问题描述】:

我正在构建一个 JSF+Facelets Web 应用程序,其中一个方法是每隔一段时间扫描一个目录并索引任何更改。此方法是应用程序范围内的 bean 的一部分。我已经构建了 TimerTask 的子类,每 X 毫秒调用一次该方法。我的问题是初始化 bean。我可以在页面上引用 bean,当我转到页面时,bean 被初始化,并按指示工作;相反,我想要的是在初始化 web 上下文时初始化 bean,这样它就不需要页面访问来启动索引方法。谷歌已经展示了一些想要这个功能的人,但是除了与 Spring 集成之外没有真正的解决方案,我真的不想仅仅为了获得这个功能而做。

我尝试过使用设置了“启动时加载”的 servlet 和 ServletContextListener 来让事情顺利进行,但无法正确设置,因为没有t FacesContext 可用,或者因为我无法从 JSF 环境中引用 bean。

有什么方法可以在 Web 应用启动时初始化 JSF bean?

【问题讨论】:

    标签: java jsf


    【解决方案1】:

    在 JSF 2+ 中,您可以使用 SystemEventListener 来处理它。您可以将其设置为对 PostConstructApplicationEvent 执行操作以对其进行初始化。

    <system-event-listener>
        <system-event-listener-class>
         listeners.SystemEventListenerImpl
        </system-event-listener-class>
        <system-event-class>
         javax.faces.event.PostConstructApplicationEvent
        </system-event-class>                       
    </system-event-listener>
    

    实现看起来像:

    public class SystemEventListenerImpl implements SystemEventListener {
    
      @Override
      public void processEvent(SystemEvent event) throws AbortProcessingException {
        Application application = (Application) event.getSource();
       //TODO
      }
    
      @Override
      public boolean isListenerForSource(Object source) {
        return (source instanceof Application);
      }
    }
    

    这将允许您做更多的事情,而不仅仅是简单地传递一个值。

    【讨论】:

    • 请注意,OP 使用的是 JSF 1.x(提出问题时 JSF 2.x 不存在)。对于 JSF 2.x 来说,有一种更简单的方法:只需一个 @ManagedBean(eager=true) @ApplicationScoped。无需 XML 或界面混乱。另见stackoverflow.com/questions/3600534/…
    • 我回去阅读了这个问题,你的回答解决了他关于 JSF 2.x 急切加载的问题的最后一部分。很好的收获。
    【解决方案2】:

    如果您的代码调用FacesContext,它将无法在与 JSF 请求生命周期关联的线程之外工作。为每个请求创建一个 FacesContext 对象,并在请求结束时处理。您可以通过static call 获取它的原因是因为它在请求开始时设置为ThreadLocal。 FacesContext 的生命周期与 ServletContext 的生命周期无关。

    也许这还不够(听起来你已经走上了这条路),但你应该能够使用 ServletContextListener 来做你想做的事。只需确保对 FacesContext 的任何调用都保存在 JSP 的请求线程中。

    web.xml:

    <listener>
        <listener-class>appobj.MyApplicationContextListener</listener-class>
    </listener>
    

    实施:

    public class MyApplicationContextListener implements ServletContextListener {
    
        private static final String FOO = "foo";
    
        public void contextInitialized(ServletContextEvent event) {
            MyObject myObject = new MyObject();
            event.getServletContext().setAttribute(FOO, myObject);
        }
    
        public void contextDestroyed(ServletContextEvent event) {
            MyObject myObject = (MyObject) event.getServletContext().getAttribute(
                    FOO);
            try {
                event.getServletContext().removeAttribute(FOO);
            } finally {
                myObject.dispose();
            }
        }
    
    }
    

    您可以通过 JSF 应用程序范围来处理此对象(如果不存在其他同名变量,则直接处理):

    <f:view>
        <h:outputText value="#{applicationScope.foo.value}" />
        <h:outputText value="#{foo.value}" />
    </f:view>
    

    如果您希望检索 JSF 托管 bean 中的对象,您可以从 ExternalContext 获取它:

    FacesContext.getCurrentInstance()
                .getExternalContext().getApplicationMap().get("foo");
    

    【讨论】:

    • 这最终解决了 - 我错过了“setAttribute”位以使其在 JSF 代码中可访问。谢谢!
    【解决方案3】:

    使用监听器或启动时加载,试试这个:http://www.thoughtsabout.net/blog/archives/000033.html

    【讨论】:

    • 我以前用过这个技巧,但它不适用于 ServletContextListener 因为你没有 facesContext = contextFactory.getFacesContext(servletContext, request, response, lifecycle); 的请求/响应;如果任何参数为空,它将失败。 load-on-startup/init 也有同样的问题。
    • 我最好/更容易在启动时创建你的类并存储它。然后当第一次访问 jsf bean 时,它将检查该类并使用它或将数据从一个复制到另一个。
    猜你喜欢
    • 2013-08-10
    • 1970-01-01
    • 2011-12-19
    • 2013-01-06
    • 2012-12-02
    • 1970-01-01
    • 1970-01-01
    • 2013-03-24
    • 2015-08-14
    相关资源
    最近更新 更多