【问题标题】:Get JSF managed bean by name in any Servlet related class在任何 Servlet 相关类中按名称获取 JSF 托管 bean
【发布时间】:2020-12-14 03:54:02
【问题描述】:

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

http://host/app/myBean/myProperty

到:

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

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

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

【问题讨论】:

标签: jsf jakarta-ee servlets jsf-2 managed-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");

另见:

【讨论】:

  • 你第二个关于注入 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:这实际上是有道理的。我会做一个测试,看看会发生什么。
    猜你喜欢
    • 2012-06-17
    • 1970-01-01
    • 2012-08-27
    • 1970-01-01
    • 2012-02-29
    相关资源
    最近更新 更多