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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6746149/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 17:07:09  来源:igfitidea点击:

How to register a JSF managed bean programmatically?

javajsfservletsmanaged-bean

提问by Zeemee

I'd like to register/add a Managed Bean class programmatically (from within a Servlet init()) into application scope. How can I do that with JSF 1.2?

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

回答by Vineet Reynolds

It is unlikely that can do do this in a programmatic manner from your application for managed beans of all scopes. BalusC has already pointed out how to do this for application scoped managed beans.

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

Having taken a look at how managed beans are registered in Mojarra 2.1 (a JSF 2.1 implementation); there isn't a lot of lot of elegant options available for programmatic registration of session and request scoped beans. Simply put, you either have to invoke the implementation specific classes, or you will have to create and destroy i.e. manage the beans yourself instead of relying on the JSF implementation to do this.

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

Populating the request and session scopes with the beans (the unmanaged way)

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

Note - This is referred to as the "unmanaged way" because you are constructing the beans, and not the container. Annotations like @PostConstructand @PreDestroywill not work, unless you process them yourself and invoke the appropriate methods. Even dependency-injection won't work.

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

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). 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 uses the classes ApplicationScopeHandler, RequestScopeHandlerand SessionScopeHandlerto obtain the values from the different scopes.

EL 表达式总是在运行时求值,所以它给了你足够的机会在求值前用 bean 填充作用域(如果你有机会的话,这允许你在脚下射击)。在 Mojarra(可能还有其他 JSF 实现)中,EL 解析器将依赖 ScopeHandler(或等效类)的服务来解析 EL 表达式值。钻嘴鱼科使用的类ApplicationScopeHandlerRequestScopeHandler并且SessionScopeHandler从不同的范围获得的值。

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.

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

Session scope population can be done (ideally using a HttpSessionListener) using:

可以使用以下方法完成会话范围填充(最好使用 a HttpSessionListener):

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

The keynamemust match the values you are using to reference the bean in EL expressions.

keyname必须使用的是引用在EL表达式豆的值相匹配。

In a similar manner, you can populate the request scope (ideally done in a filter) using:

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

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.RequestMapand com.sun.faces.context.ApplicationMapto see how the context maps are managed internally, and used by the SessionScopeHandler, RequestScopeHandlerand ApplicationScopeHandlerclasses that are static inner classes of the ScopeManager(another static inner) class of the com.sun.faces.mgbean.BeanManagerclass. The BeanManagerclass is the one that contains the managed bean registrations, and the next section discusses how to "hack into" the registration process of Mojarra.

如果您需要了解该如何工作,你应该看一看类com.sun.faces.context.SessionMapcom.sun.faces.context.RequestMap以及com.sun.faces.context.ApplicationMap怎么看上下文映射在内部管理,并通过使用SessionScopeHandlerRequestScopeHandlerApplicationScopeHandler这是静态内部类的类ScopeManager(另一种静态内部)类的在com.sun.faces.mgbean.BeanManager类。本BeanManager类是包含托管bean注册了一个,下一个部分将讨论如何“侵入”钻嘴鱼科的注册过程。

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.BeanManagerclass. It is not trivial to access the BeanManagerclass using the JSF or Servlet APIs alone. There is however the ApplicationAssociateclass of Mojarra that creates the BeanManagerinstance, and can be accessed using the getCurrentInstance()method. The other answer by Thomas already demonstartes how to register the managed bean programmatically:

Mojarra 实现中托管 bean 的注册是通过类的public void register(ManagedBeanInfo beanInfo)方法完成的com.sun.faces.mgbean.BeanManagerBeanManager单独使用 JSF 或 Servlet API访问类并非易事。然而ApplicationAssociate,有创建BeanManager实例的 Mojarra 类,可以使用该getCurrentInstance()方法访问。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 Servletfor the simple reason that the getCurrentInstancemethod relies on a ThreadLocal variable to retrieve the ApplicationAssociateinstance. The thread local variable is initialized by the com.sun.faces.application.WebappLifecycleListenerclass, so you must reproduce the mechanism used by the WebappLifecycleListenerclass, of invoking the ApplicationAssociate getInstance(ServletContext context)method, to gain access to the ApplicationAssociateinstance. 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:

上述方法有一个警告。这种方法不太可能在 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(...)

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. I would therefore suggest loading attempting to configure your servlet with a load-on-startupvalue that is higher than the one used by the FacesServlet.

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

回答by BalusC

from within a Servlet init()

从 Servlet 中 init()

So, it concerns a non-JSF request. The FacesContext#getCurrentInstance()would return nullhere, so it's of no use for you here.

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

It's good to know that JSF application scoped managed beans are basically stored as an attribute of the ServletContext. In the init()method you've the ServletContextat your hands by the inherited getServletContext()method. So, the following should do:

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

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

That's it. It'll be available in JSF by #{managedBeanName}.

而已。它将在 JSF 中由#{managedBeanName}.

回答by Thomas

Try FacesContext.currentInstance().getExternalContext().getApplicationMap().put(name, bean);, i.e. put the managed bean instance into the map using the name you want to use in your expressions.

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

Edit:

编辑:

To register the bean, try calling: ApplicationAssociate.getCurrentInstance().getBeanManager().register(...)and pass a ManagedBeanInfoyou filled.

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

回答by Pavel Sedek

The following code registers the managed bean correcly using FacesContext, but it requires the servlet request and response. You could use the code and initialize it lazily using servlet and not during the init.

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

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

回答by user1710158

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。