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

106

我正在尝试编写一个自定义Servlet(用于AJAX / JSON),其中我希望通过名称引用我的@ManagedBeans。 我希望将:

http://host/app/myBean/myProperty

映射到:

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

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

在Spring中,所有这些都太明显了,让我变得很挑剔。


我不确定你是否可以在JSF/EL之外使用这些新注释,但我建议先查看JSR 299规范:http://jcp.org/en/jsr/detail?id=299 - McDowell
其他遇到类似问题的人也可以查看https://bpcatalog.dev.java.net/ajax/jsf-ajax/(与AJAX和请求映射/处理有关,而不是按名称获取bean)。 - Konrad Garus
6个回答

271
在基于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时,此方法无效,因为只有在调用FacesServlet时,bean 才能被 JSF 视图状态识别。所以在运行在其之前的过滤器中访问@Inject @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");

参见:


10
你的第二个建议仅仅注入bean,非常简单,我完全忽略了它。像往常一样,你的回答非常到位。非常感谢你在Stack Overflow上的工作。 - jnt30
2
与此同时(截至JSF 2.2),似乎方法evaluateExpressionGet已经扩展了第三个参数,允许指定预期的类,因此不再需要强制转换。PostBean bean = context.getApplication().evaluateExpressionGet(context, "#{beanName}", PostBean.class); - Marc Juchli
1
@Marc:一直在这里。可能只是复制粘贴错误的遗留问题。答案已经被更正了。感谢您的通知。 - BalusC
1
@Tiny:它被同一线程内的JSF部件调用。 - BalusC
@BalusC 好的,但你仍然需要使用 "#{" + beanName + "}",对吧?或者我在这里有点懒惰?;-) - Jasper de Vries
显示剩余4条评论

11
我使用以下方法:
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);
}

这让我能够以有类型的方式获取返回的对象。

4
当前已接受的答案已经涵盖了这一点,而且还以更加方便的方式进行了说明(在此结构中,“Class”参数是不必要的)。 - BalusC

3

您是否尝试过类似于此链接的方法?我不确定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)设置的每个请求线程本地工件。 - McDowell
7
自从四年前 JSF 1.2 推出以来,ValueBinding 已经被废弃。请注意,这并不建议使用 ValueBinding,而是建议使用其它替代方案。 - BalusC
@BalusC:这表明了我有多么不时髦,哈哈。顺便说一句,使用搜索引擎研究技术正在变得适得其反,因为那里有很多过时的信息。 @McDowell:那确实有道理。我会进行测试,看看会发生什么。 - James P.

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中实现这个,但它不起作用。 - Fernando Pie
如果我在Quartz Job中且没有可用的FacesContext怎么办?我该如何从应用程序上下文获取Managed Bean? - Francisco Souza

0

我有同样的需求。

我使用了以下方法来实现它。

我有一个会话作用域的bean。

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

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

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

它解决了我的问题。


你使用哪种Servlet?伙计。 - Fernando Pie
1
它是HttpServlet。 - Anil

-1

我使用这个:

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

这样,您就可以轻松重构代码并跟踪使用情况,不会遇到任何问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接