11

我想以编程方式(从 Servlet init() 中)注册/添加一个托管 Bean 类到应用程序范围内。我怎样才能用 JSF 1.2 做到这一点?

4

5 回答 5

19

对于所有范围的托管 bean,不太可能以编程方式从您的应用程序中执行此操作。BalusC 已经指出了如何为应用程序范围的托管 bean 执行此操作。

了解了托管 bean 如何在 Mojarra 2.1(JSF 2.1 实现)中注册;没有很多优雅的选项可用于会话和请求范围 bean 的编程注册。简单地说,您要么必须调用实现特定的类,要么必须创建和销毁,即自己管理 bean,而不是依赖 JSF 实现来执行此操作。

使用 bean 填充请求和会话范围(非托管方式)

注意 - 这被称为“非托管方式”,因为您正在构建 bean,而不是容器。除非您自己处理它们并调用适当的方法,否则注释 like @PostConstructand@PreDestroy将不起作用。即使依赖注入也不起作用。

EL 表达式总是在运行时进行评估,因此它为您提供了足够的机会在评估之前使用 bean 填充范围(如果您有机会这样做,这允许您自己开枪)。在 Mojarra(可能还有其他 JSF 实现)中,EL 解析器将依赖 ScopeHandler(或等效类)的服务来解析 EL 表达式值。Mojarra 使用 classesApplicationScopeHandler和从不同的作用域获取值RequestScopeHandlerSessionScopeHandler

您可以在创建新会话之后或在 JSF 实现处理请求之前填充 Session 和 Request 范围的内容。

可以使用以下方法完成会话范围填充(理想情况下使用 a HttpSessionListener):

HttpSession session = request.getSession(false);
session == null ? null : session.setAttribute("<keyname>", new Bean());

keyname必须与您用于在 EL 表达式中引用 bean 的值匹配。

以类似的方式,您可以使用以下方法填充请求范围(最好在过滤器中完成):

ServletRequest request = ... // get the reference to the servlet request object
request.setAttribute("<keyname>", new Bean());

如果您需要了解它是如何工作的,您应该查看 classes com.sun.faces.context.SessionMapcom.sun.faces.context.RequestMapcom.sun.faces.context.ApplicationMap了解上下文映射是如何在内部管理的,以及如何使用的SessionScopeHandlerRequestScopeHandler以及ApplicationScopeHandler作为ScopeManager(另一个静态内部)类的静态内部类的类com.sun.faces.mgbean.BeanManager班级。该类BeanManager是包含托管 bean 注册的类,下一节将讨论如何“侵入”Mojarra 的注册过程。

使用 Mojarra 类注册 bean

Mojarra 实现中托管 bean 的注册是通过类的public void register(ManagedBeanInfo beanInfo)方法完成的com.sun.faces.mgbean.BeanManager。单独使用 JSF 或 Servlet API访问BeanManager该类并非易事。然而,有ApplicationAssociate一个创建BeanManager实例的 Mojarra 类,并且可以使用该getCurrentInstance()方法访问。Thomas 的另一个答案已经演示了如何以编程方式注册托管 bean:

ApplicationAssociate.getCurrentInstance().getBeanManager().register(...)

上述方法有一个警告。这种方法不太可能在 init 方法中起作用,Servlet原因很简单,因为该getCurrentInstance方法依赖于 ThreadLocal 变量来检索ApplicationAssociate实例。线程局部变量由com.sun.faces.application.WebappLifecycleListener类初始化,因此您必须重现WebappLifecycleListener类使用的机制,即调用ApplicationAssociate getInstance(ServletContext context)方法,以获得对ApplicationAssociate实例的访问。因此,如果您愿意使用 Mojarra 特定的类,以下代码可能(因为我没有尝试使用它)是一个更好的代码:

ServletContext sc = ... //get the ServletContext reference;
ApplicationAssociate.getInstance(sc).getBeanManager().register(...)

您仍然必须注意这种机制引起的怪癖,因为很可能某些 Mojarra 类和实例不会在您的 Servlet 之前加载或初始化。因此,我建议加载尝试配置您的 servlet,其load-on-startup值高于FacesServlet.

于 2011-07-19T12:32:10.027 回答
14

从 Servlet 内部init()

因此,它涉及非 JSF 请求。会FacesContext#getCurrentInstance()回到null这里,所以这里对你没用。

很高兴知道 JSF 应用程序范围的托管 bean 基本上存储为ServletContext. 在init()方法中,您可以ServletContext通过继承的方法来掌握getServletContext()。因此,应该执行以下操作:

@Override
public void init() {
    getServletContext().setAttribute("managedBeanName", new BackingBean());
}

而已。它将在 JSF 中可用#{managedBeanName}

于 2011-07-19T11:37:43.887 回答
3

尝试FacesContext.currentInstance().getExternalContext().getApplicationMap().put(name, bean);,即使用您要在表达式中使用的名称将托管 bean 实例放入映射中。

编辑:

要注册 bean,请尝试调用:ApplicationAssociate.getCurrentInstance().getBeanManager().register(...)并传递一个ManagedBeanInfo您填写的内容。

于 2011-07-19T11:22:06.627 回答
1

以下代码使用 正确注册托管 bean FacesContext,但它需要 servlet 请求和响应。您可以使用代码并使用 servlet 懒惰地初始化它,而不是在初始化期间。

用法:

UserBean ub = (UserBean) 
    Example.getBean(servletRequest, servletResponse, "user", UserBean.class);

资源:

import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import javax.faces.FactoryFinder;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.context.FacesContextFactory;
import javax.faces.lifecycle.LifecycleFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

class Example {

    public static Object getBean(HttpServletRequest request, HttpServletResponse response, String beanName, Class expectedType){
        FacesContext ctx = getFacesContext(request, response);
        ValueExpression vex = ctx.getApplication().getExpressionFactory().createValueExpression(ctx.getELContext(), "#{"+beanName+"}", expectedType);
        return vex.getValue(ctx.getELContext());
    }

    private static FacesContext getFacesContext(HttpServletRequest request, HttpServletResponse response) {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        if (facesContext == null) {
            facesContext = ((FacesContextFactory) FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY)).
                getFacesContext(request.getSession().getServletContext(), request, response, 
                ((LifecycleFactory) FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY))
                .getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE));

            InnerFacesContext.setFacesContextAsCurrentInstance(facesContext);
            facesContext.setViewRoot(facesContext.getApplication().getViewHandler().createView(facesContext, ""));
        }
        return facesContext;
    }

    private abstract static class InnerFacesContext extends FacesContext {
        protected static void setFacesContextAsCurrentInstance(FacesContext facesContext) {
            FacesContext.setCurrentInstance(facesContext);
        }
    }
}
于 2014-09-04T15:54:01.987 回答
-1

假设我已经注册了我的 bean 使用

ApplicationAssociate.getInstance(sc).getBeanManager().register(...)

现在它工作正常,但是服务器重新启动并且我的 bean 被破坏了,如何在启动时注册相同的 bean。

于 2015-10-21T10:09:36.983 回答