简体   繁体   English

如何以编程方式注册 JSF 托管 bean?

[英]How to register a JSF managed bean programmatically?

I'd like to register/add a Managed Bean class programmatically (from within a Servlet init()) into application scope.我想以编程方式(从 Servlet init() 中)将托管 Bean class 注册/添加到应用程序 scope 中。 How can I do that with JSF 1.2?如何使用 JSF 1.2 做到这一点?

It is unlikely that can do do this in a programmatic manner from your application for managed beans of all scopes.对于所有范围的托管 bean,不太可能以编程方式从您的应用程序中执行此操作。 BalusC has already pointed out how to do this for application scoped managed beans. BalusC 已经指出了如何为应用程序范围的托管 bean 执行此操作。

Having taken a look at how managed beans are registered in Mojarra 2.1 (a JSF 2.1 implementation);查看了如何在 Mojarra 2.1 中注册托管 bean(JSF 2.1 实现); there isn't a lot of lot of elegant options available for programmatic registration of session and request scoped beans.没有很多优雅的选项可用于 session 的编程注册和请求范围 bean。 Simply put, you either have to invoke the implementation specific classes, or you will have to create and destroy ie manage the beans yourself instead of relying on the JSF implementation to do this.简而言之,您要么必须调用实现特定的类,要么必须创建和销毁,即自己管理 bean,而不是依赖 JSF 实现来执行此操作。

Populating the request and session scopes with the beans (the unmanaged way)使用 bean 填充请求和 session 范围(非托管方式)

Note - This is referred to as the "unmanaged way" because you are constructing the beans, and not the container.注意 - 这被称为“非托管方式”,因为您正在构建 bean,而不是容器。 Annotations like @PostConstruct and @PreDestroy will not work, unless you process them yourself and invoke the appropriate methods. @PostConstruct@PreDestroy之类的注解将不起作用,除非您自己处理它们并调用适当的方法。 Even dependency-injection won't work.即使依赖注入也不起作用。

EL expressions are always evaluated at runtime, so it gives you enough opportunity to populate the scope with the beans before evaluation (which allows for shooting yourself in the foot, if you have the chance to do so). EL 表达式始终在运行时进行评估,因此它为您提供了足够的机会在评估之前使用 bean 填充 scope(如果您有机会这样做的话,这可以让您自己开枪)。 In Mojarra (and possibly other JSF implementations), the EL resolver will rely on the services of a ScopeHandler (or an equivalent class) to resolve the EL expression values.在 Mojarra(以及可能的其他 JSF 实现)中,EL 解析器将依赖 ScopeHandler(或等效类)的服务来解析 EL 表达式值。 Mojarra uses the classes ApplicationScopeHandler , RequestScopeHandler and SessionScopeHandler to obtain the values from the different scopes. Mojarra 使用类ApplicationScopeHandlerRequestScopeHandlerSessionScopeHandler来获取来自不同范围的值。

You can populate the contents of the Session and Request scopes after after a new session is created, or before a request is processed by the JSF implementation.您可以在创建新的 session 之后或在 Z798476CFD70310827337C1DDFDCC4D664Z 实现处理请求之前填充 Session 和请求范围的内容。

Session scope population can be done (ideally using a HttpSessionListener ) using: Session scope 填充可以使用以下方法完成(理想情况下使用HttpSessionListener ):

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

The keyname must match the values you are using to reference the bean in EL expressions. keyname必须与您用于在 EL 表达式中引用 bean 的值匹配。

In a similar manner, you can populate the request scope (ideally done in a filter) using:以类似的方式,您可以使用以下命令填充请求 scope(最好在过滤器中完成):

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

If you need to understand how this works, you should take a look at the classes com.sun.faces.context.SessionMap , com.sun.faces.context.RequestMap and com.sun.faces.context.ApplicationMap to see how the context maps are managed internally, and used by the SessionScopeHandler , RequestScopeHandler and ApplicationScopeHandler classes that are static inner classes of the ScopeManager (another static inner) class of the com.sun.faces.mgbean.BeanManager class. If you need to understand how this works, you should take a look at the classes com.sun.faces.context.SessionMap , com.sun.faces.context.RequestMap and com.sun.faces.context.ApplicationMap to see how the context maps are managed internally, and used by the SessionScopeHandler , RequestScopeHandler and ApplicationScopeHandler classes that are static inner classes of the ScopeManager (another static inner) class of the com.sun.faces.mgbean.BeanManager class. The BeanManager class is the one that contains the managed bean registrations, and the next section discusses how to "hack into" the registration process of Mojarra. BeanManager class 是包含托管 bean 注册的那个,下一节将讨论如何“侵入”Mojarra 的注册过程。

Using the Mojarra classes to register the beans使用 Mojarra 类注册 bean

Registration of managed beans in the Mojarra implementation is done by the public void register(ManagedBeanInfo beanInfo) method of the com.sun.faces.mgbean.BeanManager class. Mojarra 实现中托管 bean 的注册由 com.sun.faces.mgbean.BeanManager class 的public void register(ManagedBeanInfo beanInfo) com.sun.faces.mgbean.BeanManager完成。 It is not trivial to access the BeanManager class using the JSF or Servlet APIs alone.单独使用 JSF 或 Servlet API 访问BeanManager class 并非易事。 There is however the ApplicationAssociate class of Mojarra that creates the BeanManager instance, and can be accessed using the getCurrentInstance() method.然而,Mojarra 的ApplicationAssociate class 创建了BeanManager实例,并且可以使用getCurrentInstance()方法访问。 The other answer by Thomas already demonstartes how to register the managed bean programmatically: Thomas 的另一个答案已经演示了如何以编程方式注册托管 bean:

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

There is a caveat with the above approach.上述方法有一个警告。 It is unlikely that this approach will work in the init method of Servlet for the simple reason that the getCurrentInstance method relies on a ThreadLocal variable to retrieve the ApplicationAssociate instance.这种方法不太可能在Servlet的 init 方法中起作用,原因很简单, getCurrentInstance方法依赖于 ThreadLocal 变量来检索ApplicationAssociate实例。 The thread local variable is initialized by the com.sun.faces.application.WebappLifecycleListener class, so you must reproduce the mechanism used by the WebappLifecycleListener class, of invoking the ApplicationAssociate getInstance(ServletContext context) method, to gain access to the ApplicationAssociate instance. The thread local variable is initialized by the com.sun.faces.application.WebappLifecycleListener class, so you must reproduce the mechanism used by the WebappLifecycleListener class, of invoking the ApplicationAssociate getInstance(ServletContext context) method, to gain access to the ApplicationAssociate instance. The following code therefore, might be (as I have not attempted using it) a better one, if you are willing to use Mojarra specific classes:因此,如果您愿意使用 Mojarra 特定的类,以下代码可能(因为我没有尝试使用它)更好:

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

You must still watch out for quirks arising out of this mechanism as it is quite possible that some of the Mojarra classes and instances would not have been loaded or initialized before your Servlet.您仍然必须注意这种机制引起的怪癖,因为很可能某些 Mojarra 类和实例不会在您的 Servlet 之前加载或初始化。 I would therefore suggest loading attempting to configure your servlet with a load-on-startup value that is higher than the one used by the FacesServlet .因此,我建议加载尝试配置您的 servlet,其load-on-startup值高于FacesServlet使用的值。

from within a Servlet init()从 Servlet init()

So, it concerns a non-JSF request.因此,它涉及非 JSF 请求。 The FacesContext#getCurrentInstance() would return null here, so it's of no use for you here. FacesContext#getCurrentInstance()会在这里返回null ,所以在这里对你没有用。

It's good to know that JSF application scoped managed beans are basically stored as an attribute of the ServletContext .很高兴知道 JSF 应用程序范围的托管 bean 基本上存储为ServletContext的属性。 In the init() method you've the ServletContext at your hands by the inherited getServletContext() method.init()方法中,您可以通过继承的getServletContext()方法获得ServletContext So, the following should do:因此,应该执行以下操作:

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

That's it.而已。 It'll be available in JSF by #{managedBeanName} .它将由#{managedBeanName}在 JSF 中提供。

Try FacesContext.currentInstance().getExternalContext().getApplicationMap().put(name, bean);试试FacesContext.currentInstance().getExternalContext().getApplicationMap().put(name, bean); , ie put the managed bean instance into the map using the name you want to use in your expressions. ,即使用您要在表达式中使用的名称将托管 bean 实例放入 map。

Edit:编辑:

To register the bean, try calling: ApplicationAssociate.getCurrentInstance().getBeanManager().register(...) and pass a ManagedBeanInfo you filled.要注册 bean,请尝试调用: ApplicationAssociate.getCurrentInstance().getBeanManager().register(...)并传递您填写的ManagedBeanInfo

The following code registers the managed bean correcly using FacesContext , but it requires the servlet request and response.以下代码使用FacesContext正确注册托管 bean,但它需要 servlet 请求和响应。 You could use the code and initialize it lazily using servlet and not during the init.您可以使用代码并使用 servlet 懒惰地初始化它,而不是在初始化期间。

Usage:用法:

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

Source:资源:

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);
        }
    }
}

Say I have registered my beans using假设我已经注册了我的 bean 使用

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

Now It is working fine, But then there is a server restart and my beans are destroyed, How on startup I can get the same bean registered.现在它工作正常,但是服务器重新启动并且我的 bean 被破坏了,如何在启动时注册相同的 bean。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM