在任何 Servlet 相关类中按名称获取 JSF 托管 bean

Posted

技术标签:

【中文标题】在任何 Servlet 相关类中按名称获取 JSF 托管 bean【英文标题】:Get JSF managed bean by name in any Servlet related class 【发布时间】:2011-02-07 15:41:19 【问题描述】:

我正在尝试编写一个自定义 servlet(用于 AJAX/JSON),我想在其中按名称引用我的 @ManagedBeans。我希望映射:

http://host/app/myBean/myProperty

到:

@ManagedBean(name="myBean")
public class MyBean 
    public String getMyProperty();

是否可以从常规 servlet 中按名称加载 bean?有没有我可以使用的 JSF servlet 或助手?

我似乎被春天宠坏了,这一切都太明显了。

【问题讨论】:

我不确定您是否可以在 JSF/EL 之外使用这些新注释,但我先看看 JSR 299 规范:jcp.org/en/jsr/detail?id=299 其他有类似问题的人也可以查看bpcatalog.dev.java.net/ajax/jsf-ajax(与AJAX和请求映射/处理有关,不按名称获取bean) 【参考方案1】:

在基于 servlet 的工件中,例如 @WebServlet@WebFilter@WebListener,您可以通过以下方式获取“普通版”JSF @ManagedBean @RequestScoped

Bean bean = (Bean) request.getAttribute("beanName");

@ManagedBean @SessionScoped

Bean bean = (Bean) request.getSession().getAttribute("beanName");

@ManagedBean @ApplicationScoped

Bean bean = (Bean) getServletContext().getAttribute("beanName");

请注意,这预先要求 bean 已经由 JSF 预先自动创建。否则这些将返回null。然后您需要手动创建 bean 并使用 setAttribute("beanName", bean)


如果您能够使用 CDI @Named 而不是自 JSF 2.3 弃用的 @ManagedBean,那么它会更加容易,特别是因为您不再需要手动创建 bean:

@Inject
private Bean bean;

请注意,当您使用 @Named @ViewScoped 时,这将不起作用,因为 bean 只能由 JSF 视图状态标识,并且仅在调用 FacesServlet 时可用。所以在之前运行的过滤器中,访问@Injected @ViewScoped 总是会抛出ContextNotActiveException


只有在@ManagedBean里面,才能使用@ManagedProperty

@ManagedProperty("#bean")
private Bean bean;

请注意,这在 @Named@WebServlet 或任何其他工件中不起作用。它真的只在@ManagedBean 内有效。


如果您不在@ManagedBean 中,但FacesContext 很容易获得(即FacesContext#getCurrentInstance() 不返回null),您也可以使用Application#evaluateExpressionGet()

FacesContext context = FacesContext.getCurrentInstance();
Bean bean = context.getApplication().evaluateExpressionGet(context, "#beanName", Bean.class);

可以方便如下:

@SuppressWarnings("unchecked")
public static <T> T findBean(String beanName) 
    FacesContext context = FacesContext.getCurrentInstance();
    return (T) context.getApplication().evaluateExpressionGet(context, "#" + beanName + "", Object.class);

并且可以如下使用:

Bean bean = findBean("bean");

另见:

Backing beans (@ManagedBean) or CDI Beans (@Named)?

【讨论】:

你第二个关于注入 bean 的建议非常简单,我完全忽略了它。与往常一样,您的回答非常中肯。非常感谢您在 SO 上所做的工作。 与此同时(从 JSF 2.2 开始),evaluateExpressionGet 方法似乎扩展了第三个参数,该参数允许指定预期的类,因此不再需要强制转换。 PostBean bean = context.getApplication().evaluateExpressionGet(context, "#beanName", PostBean.class); @Marc:从一开始就在。我猜这只是复制粘贴错误的遗留物。答案已更正。感谢您的通知。 FacesContext 可用,即使 static 实用方法 findBean() 是在普通 Java 类中定义的。它如何在不受 JSF 管理的普通 Java 类中可用? @Tiny:它又被同一线程中的 JSF 工件调用。【参考方案2】:

我也有同样的要求。

我已使用以下方式获取它。

我有会话范围的 bean。

@ManagedBean(name="mb")
@SessionScopedpublic 
class ManagedBean 
     --------

我在我的 servlet doPost() 方法中使用了以下代码。

ManagedBean mb = (ManagedBean) request.getSession().getAttribute("mb");

它解决了我的问题。

【讨论】:

你使用什么样的servlet?伴侣 是HttpServlet。【参考方案3】:

你可以通过传递名称来获取托管bean:

public static Object getBean(String beanName)
    Object bean = null;
    FacesContext fc = FacesContext.getCurrentInstance();
    if(fc!=null)
         ELContext elContext = fc.getELContext();
         bean = elContext.getELResolver().getValue(elContext, null, beanName);
    

    return bean;

【讨论】:

我尝试从 servlet 执行此操作,但它不起作用。【参考方案4】:

我用这个:

public static <T> T getBean(Class<T> clazz) 
    try 
        String beanName = getBeanName(clazz);
        FacesContext facesContext = FacesContext.getCurrentInstance();
        return facesContext.getApplication().evaluateExpressionGet(facesContext, "#" + beanName + "", clazz);
    //return facesContext.getApplication().getELResolver().getValue(facesContext.getELContext(), null, nomeBean);
     catch (Exception ex) 
        return null;
    


public static <T> String getBeanName(Class<T> clazz) 
    ManagedBean managedBean = clazz.getAnnotation(ManagedBean.class);
    String beanName = managedBean.name();

    if (StringHelper.isNullOrEmpty(beanName)) 
        beanName = clazz.getSimpleName();
        beanName = Character.toLowerCase(beanName.charAt(0)) + beanName.substring(1);
    

    return beanName;

然后调用:

MyManageBean bean = getBean(MyManageBean.class);

这样您就可以毫无问题地重构代码并跟踪使用情况。

【讨论】:

【参考方案5】:

我使用以下方法:

public static <T> T getBean(final String beanName, final Class<T> clazz) 
    ELContext elContext = FacesContext.getCurrentInstance().getELContext();
    return (T) FacesContext.getCurrentInstance().getApplication().getELResolver().getValue(elContext, null, beanName);

这允许我以键入的方式获取返回的对象。

【讨论】:

这已经被当前接受的答案所涵盖,甚至以更方便的方式(Class 参数在此构造中是不必要的)。【参考方案6】:

您是否尝试过类似此链接的方法?我不确定createValueBinding() 是否仍然可用,但是应该可以从普通的旧 Servlet 访问这样的代码。这确实需要 bean 已经存在。

http://www.coderanch.com/t/211706/JSF/java/access-managed-bean-JSF-from

 FacesContext context = FacesContext.getCurrentInstance();  
 Application app = context.getApplication();
 // May be deprecated
 ValueBinding binding = app.createValueBinding("#" + expr + ""); 
 Object value = binding.getValue(context);

【讨论】:

这可能在常规 servlet 中不起作用。 FacesContext 是由 JSF 生命周期(通常是 FacesServlet)设置的按请求线程本地工件。 ValueBinding 自 4 年前的 JSF 1.2 起已弃用。 @BalusC:这表明我是最新的,哈哈。在旁注中,使用搜索引擎来研究技术结果会适得其反,因为所有旧信息都在那里。 @McDowell:这实际上是有道理的。我会做一个测试,看看会发生什么。

以上是关于在任何 Servlet 相关类中按名称获取 JSF 托管 bean的主要内容,如果未能解决你的问题,请参考以下文章

在 JSF 中按 ID 查找组件

从 java.lang.Object 访问 clone()

在 PrimeFaces 3.4 JSF 2.0 中按 id 查找组件

在jsf组件中按下回车键时限制Ajax机制

JSF 获取托管 bean 中的当前操作

维护常用类的 Servlet [关闭]