我想以编程方式(从 Servlet init() 中)注册/添加一个托管 Bean 类到应用程序范围内。我怎样才能用 JSF 1.2 做到这一点?
5 回答
对于所有范围的托管 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和从不同的作用域获取值RequestScopeHandler。SessionScopeHandler
您可以在创建新会话之后或在 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.SessionMap,com.sun.faces.context.RequestMap并com.sun.faces.context.ApplicationMap了解上下文映射是如何在内部管理的,以及如何使用的SessionScopeHandler,RequestScopeHandler以及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.
从 Servlet 内部
init()
因此,它涉及非 JSF 请求。会FacesContext#getCurrentInstance()回到null这里,所以这里对你没用。
很高兴知道 JSF 应用程序范围的托管 bean 基本上存储为ServletContext. 在init()方法中,您可以ServletContext通过继承的方法来掌握getServletContext()。因此,应该执行以下操作:
@Override
public void init() {
getServletContext().setAttribute("managedBeanName", new BackingBean());
}
而已。它将在 JSF 中可用#{managedBeanName}。
尝试FacesContext.currentInstance().getExternalContext().getApplicationMap().put(name, bean);,即使用您要在表达式中使用的名称将托管 bean 实例放入映射中。
编辑:
要注册 bean,请尝试调用:ApplicationAssociate.getCurrentInstance().getBeanManager().register(...)并传递一个ManagedBeanInfo您填写的内容。
以下代码使用 正确注册托管 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);
}
}
}
假设我已经注册了我的 bean 使用
ApplicationAssociate.getInstance(sc).getBeanManager().register(...)
现在它工作正常,但是服务器重新启动并且我的 bean 被破坏了,如何在启动时注册相同的 bean。