【问题标题】:How to log managed bean name and action method being invoked in a servlet filter如何记录在 servlet 过滤器中调用的托管 bean 名称和操作方法
【发布时间】:2013-04-26 04:17:05
【问题描述】:

我正在开发基于 JSF 2 Mojarra 构建的 Web 应用程序。我需要通过我的 servlet 过滤器记录一些仪器信息。为此,我还需要知道调用了哪个 ManagedBean 和哪个方法。

有没有办法获取这些信息?我无法访问FacesContext,因为在请求到达 Faces Servlet 之前调用了过滤器。

【问题讨论】:

  • 我不知道你是否可以只使用 Servlet Filter 来做到这一点,但最好使用 PhaseListener。请参阅Debug JSF lifecycle 以获取有关此问题的完整教程。
  • 请求处理周期仅从调用 Faces Servlet 开始。虽然,您可以在不使用FacesContext 的情况下获得会话范围的 bean,但是现在尝试获得其他类似请求范围的 bean 还为时过早。什么样的信息是“仪器信息”?我也认为像 Luiggi 建议的那样使用相位监听器会更好。
  • @LuiggiMendoza - 但是阶段监听器在 FacesServlet 之后开始起作用。我的要求是记录请求到达我的服务器和响应离开我的服务器之间的时间。使用相位监听器我不会得到确切的时间。
  • 好吧,如果您不知道自己调用了什么,就无法知道确切的托管 bean 和方法。请注意,此信息由FacesContext 提供,没有其他人提供。尽管如此,如果您想以这种方式执行此操作,您应该必须在 每个请求 上传递参数,标记托管 bean 名称和调用的方法(是的,非常笨拙)。
  • @LuiggiMendoza 只是好奇。它应该在请求正文中的某处可用,如果没有,那么 FacesServlet 将如何委托给正确的 ManagedBean。

标签: java jsf jsf-2 servlet-filters


【解决方案1】:

我了解到您想要记录被调用的UICommand 组件。 servlet 过滤器不适合这种情况,因为它无法访问FacesContext,更不用说您最终需要遍历的UIViewRootFacesContext(以及固有的UIViewRoot 等)是由FacesServlet 创建的,它是一个体面的servlet,完全符合servlet spec 在所有过滤器之后调用的。因此,不可能在 servlet 过滤器中得到FacesContext 的手。确实,有ways 可以创建自己的FacesContext 实例,但如果存在实现需求的“正确方法”,则绝对不建议这样做。

您应该为这项工作使用正确的工具,在这种特殊情况下是相位监听器。这是一个阶段监听器的外观和应该如何注册的启动示例:

public class MyPhaseListener implements PhaseListener {

    @Override
    public PhaseId getPhaseId() {
        return PhaseId.RESTORE_VIEW;
    }

    @Override
    public void beforePhase(PhaseEvent event) {
        // Do your job here which should run right before the RESTORE_VIEW phase.
    }

    @Override
    public void afterPhase(PhaseEvent event) {
        // Do your job here which should run right after the RESTORE_VIEW phase.
    }

}

要让它运行,在faces-config.xml注册它如下:

<lifecycle>
    <phase-listener>com.example.MyPhaseListener</phase-listener>
</lifecycle>

您可以将getPhaseId() 结果更改为您的见解,例如PhaseId.RENDER_RESPONSE,那么阶段监听器将在渲染响应阶段之前和之后启动。

这是一个具体的启动示例,它完成了您正在寻找的工作(查找被调用的命令组件并记录其操作方法表达式):

public class InvokedCommandComponentLogger implements PhaseListener {

    @Override
    public PhaseId getPhaseId() {
        return PhaseId.RESTORE_VIEW;
    }

    @Override
    public void beforePhase(PhaseEvent event) {
        // NOOP. The view hasn't been restored yet at that point, so the component tree wouldn't be available anyway.
    }

    @Override
    public void afterPhase(PhaseEvent event) {
        FacesContext context = event.getFacesContext();

        if (context.isPostback()) {
            UICommand component = findInvokedCommandComponent(context);

            if (component != null) {
                String methodExpression = component.getActionExpression().getExpressionString();
                System.out.println("Method expression of the action being invoked: " + methodExpression);
            }
        }
    }

    private UICommand findInvokedCommandComponent(FacesContext context) {
        Map<String, String> params = context.getExternalContext().getRequestParameterMap();
        Set<String> clientIds = new HashSet<>();

        if (context.getPartialViewContext().isAjaxRequest()) {
            clientIds.add(params.get("javax.faces.source")); // This covers <f:ajax> inside UICommand.
        } else {
            for (Entry<String, String> entry : params.entrySet()) {
                if (entry.getKey().equals(entry.getValue())) { // This covers UIForm and UICommand components.
                    clientIds.add(entry.getKey());
                }
            }
        }

        EnumSet<VisitHint> hints = EnumSet.of(VisitHint.SKIP_UNRENDERED);
        final UICommand[] found = new UICommand[1];
        context.getViewRoot().visitTree(VisitContext.createVisitContext(context, clientIds, hints), new VisitCallback() {
            @Override
            public VisitResult visit(VisitContext context, UIComponent target) {
                if (target instanceof UICommand) {
                    found[0] = (UICommand) target;
                    return VisitResult.COMPLETE;
                } else {
                    return VisitResult.ACCEPT;
                }
            }
        });

        return found[0];
    }

}

【讨论】:

  • 谢谢!我采用您的方法并在阶段侦听器中捕获方法表达式。我进一步将其添加到请求中,最后在请求处理后将其放入我的过滤器中。
  • 出现的一个问题是当我使用复合组件时。我得到的是#{cc.attrs.action},而不是实际的方法表达式,这是我的复合实现中的表达式。有解决办法吗?
猜你喜欢
  • 2011-06-14
  • 1970-01-01
  • 2012-02-28
  • 2013-02-19
  • 1970-01-01
  • 2013-06-20
  • 2012-01-02
  • 2015-05-16
  • 1970-01-01
相关资源
最近更新 更多