我希望能够在Servlet的init()方法中通过编程的方式向应用程序作用域注册/添加Managed Bean类。如何使用JSF 1.2实现这一功能?
我希望能够在Servlet的init()方法中通过编程的方式向应用程序作用域注册/添加Managed Bean类。如何使用JSF 1.2实现这一功能?
对于所有范围的托管 Bean,从您的应用程序以编程方式完成此操作是不太可能的。BalusC已经指出了如何为应用程序作用域托管 Bean 完成此操作。
查看了 Mojarra 2.1(JSF 2.1 实现)中托管 Bean 的注册方式后,我们发现没有太多优雅的选项可以用于编程式注册会话范围和请求范围的 Bean。简单来说,您只能调用特定于实现的类,或者自己创建和销毁即管理这些 Bean,而不是依靠 JSF 实现来管理。
将请求和会话范围填充为 Bean(非托管方式)
注意-这被称为“非托管方式”,因为您正在构建 Bean,而不是容器。注释,例如 @PostConstruct
和 @PreDestroy
不起作用,除非您自己处理它们并调用适当的方法。甚至依赖注入也无法使用。
EL 表达式始终在运行时计算,因此它为您提供足够的机会在评估之前填充范围与 Bean(如果您有机会这样做,这会导致自找麻烦)。在 Mojarra(及其他一些 JSF 实现)中,EL 解析器将依赖 ScopeHandler(或等效类)的服务来解决 EL 表达式值。Mojarra 使用 ApplicationScopeHandler
、RequestScopeHandler
和 SessionScopeHandler
类从不同的范围获取值。
您可以在创建新会话后或 JSF 实现处理请求之前填充会话和请求范围的内容。
最好使用 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());
如果您需要了解其工作原理,可以查看类com.sun.faces.context.SessionMap
、com.sun.faces.context.RequestMap
和com.sun.faces.context.ApplicationMap
,以查看上下文映射是如何在内部进行管理的,并由SessionScopeHandler
、RequestScopeHandler
和ApplicationScopeHandler
类使用,这些类是com.sun.faces.mgbean.BeanManager
类的静态内部类。而BeanManager
类则包含托管bean的注册信息,接下来的一节将讨论如何“突破”Mojarra的注册过程。com.sun.faces.mgbean.BeanManager
类的public void register(ManagedBeanInfo beanInfo)
方法完成的。仅使用JSF或Servlet API访问BeanManager
类并不容易。但是,在Mojarra中有一个ApplicationAssociate
类创建了BeanManager
实例,并可以使用getCurrentInstance()
方法访问。Thomas的另一个答案已经演示了如何以编程方式注册托管bean。ApplicationAssociate.getCurrentInstance().getBeanManager().register(...)
上述方法存在一个注意事项。在Servlet
的init方法中,这种方法不太可能起作用,因为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(...)
你必须仍然注意由于这种机制可能导致的一些问题,因为有可能在你的Servlet之前没有加载或初始化某些Mojarra类和实例。因此,我建议尝试使用比使用的load-on-startup
值更高的值来配置你的servlet。
在 Servlet 的 init() 方法中
因此,它涉及到一个非 JSF 请求。在这里,FacesContext#getCurrentInstance()
将返回 null
,因此对您没有用处。
了解到 JSF 应用程序范围的管理 bean 基本上是作为 ServletContext
的属性存储的。在 init()
方法中,您可以通过继承的getServletContext()
方法来获取ServletContext
。所以,以下代码应该可以完成:
@Override
public void init() {
getServletContext().setAttribute("managedBeanName", new BackingBean());
}
就是这样。它将在JSF中可用,通过#{managedBeanName}
。
FacesContext.currentInstance().getExternalContext().getApplicationMap().put(name, bean);
,将托管bean实例放入地图中,使用您想在表达式中使用的名称。ApplicationAssociate.getCurrentInstance().getBeanManager().register(...)
并传递您填写的ManagedBeanInfo
。FacesContext
对象的,因此使用getCurrentInstance()
方法会返回null。我不确定ApplicationAssociate
对象是否可用,但这样做实际上是一种不好的编程习惯,因为它将代码与特定的JSF实现(在本例中为Mojarra)紧密耦合。您应该尽量避免导入/使用特定于实现的代码,并优先使用抽象API进行编程(就像您为JDBC、Servlet等所做的一样)。 - BalusCregister
,而 @Joshi 表示注册意味着更多地将其放入应用程序范围内,这点我并不太在意。 - ThomasFacesContext
正确注册托管bean,但需要servlet请求和响应。您可以使用该代码并在servlet中懒惰地初始化,而不是在init期间进行初始化。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。