doFilter调用两次,打算行为?

时间:2013-05-20 09:50:46

标签: java java-ee servlets glassfish servlet-filters

我正在编写Java EE servlet教程并尝试mood example。我注意到,一旦servlet调用在链中,第二次不是,就会调用doFilter两次。

我在TimeOfDayFilter.java和MoodServlet.java中添加了一些printlns来显示它。

TimeOfDayFilter.java:

    ...
    System.out.println("TimeOfDay before"); //added
    chain.doFilter(req, res);
    System.out.println("TimeOfDay after"); //added
    ...

MoodServlet.java:

    ...
    response.setContentType("text/html;charset=UTF-8");

    System.out.println("MoodServlet"); //added

    PrintWriter out = response.getWriter();
    ...

调用servlet时glassfish服务器(3.1)窗口的结果如下:

    INFO: mood was successfully deployed in 406 milliseconds.
    INFO: TimeOfDay before
    INFO: MoodServlet
    INFO: TimeOfDay after
    INFO: TimeOfDay before
    INFO: TimeOfDay after

这是预期的行为吗?如果是这样,额外通话的原因是什么?

8 个答案:

答案 0 :(得分:4)

chain.doFilter(request,response);

这会将控件传递给与过滤器关联的servlet。 但是在执行相应的servlet之后,控件返回到上一行的末尾,然后执行当前doFilter()中的所有行。

如果要将控件永久传递给servlet而不让它返回过滤器,只需添加一个

return;

在当前过滤器中的chain.doFilter(request,response)行的末尾。

答案 1 :(得分:3)

每个请求Filter.doFilter方法被调用一次。您可以在调用链中的其他过滤器之前以及之后(按照过滤器链中指定的顺序,按照web.xml filter-mapping的顺序)执行一些代码, 类似于以下示例:

public MyFilter implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response,
           FilterChain chain) 
           throws IOException, ServletException
    {
        codeToExecuteBeforeOtherFiltersInTheChain(request, response);

        chain.doFilter(request, response);

        codeToExecuteAfterOtherFiltersInTheChain(request, response);

    }
}

如果您的过滤器配置为分派REQUESTFORWARD请求,则对于原始请求,MyFilter.doFilter方法将被调用一次,如果请求已经{{3} }:

使用web.xml文件配置过滤器映射:

...
<filter-mapping>
      <filter-name>MyFilter</filter-name>
      <url-pattern>/*</url-pattern>
      <dispatcher>REQUEST</dispatcher>
      <dispatcher>FORWARD</dispatcher>
</filter-mapping>
...

使用forwarded注释配置过滤器映射:

@WebFilter(urlPatterns = "/*", dispatcherTypes = {
    DispatcherType.REQUEST, DispatcherType.FORWARD
}) public MyFilter implements Filter {
    ...
}

要检查请求是否已转发,可以使用此处描述的请求属性:@WebFilter

有关过滤器的详细信息,请参见:How to know when the request is forwarded in a RequestWrapper object

答案 2 :(得分:2)

是的,Filter在生命周期中执行两次,当客户端请求进入servlet时第一次调用,第二次在执行servlet后向客户端提供响应时调用。

执行的顺序看起来像这样。

Filter lifecycle

答案 3 :(得分:1)

调用过滤器两次的原因是响应创建中使用的图像,例如

out.println("<img src=\"resources/images/duke.snooze.gif\" alt=\"Duke sleeping\"/><br/>");

请参阅日志输出

2016-01-16T11:25:34.894+0100|Info: TimeOfDay doFilter method before sending to chain

2016-01-16T11:25:34.895+0100|Info: MoodServlet get method called

2016-01-16T11:25:34.895+0100|Info: TimeOfDay doFilter method after sending to chain

2016-01-16T11:25:34.942+0100|Info: TimeOfDay doFilter method before sending to chain

2016-01-16T11:25:34.942+0100|Info: TimeOfDay doFilter method after sending to chain
img标签中的 src只不过是服务器要处理的第二个请求。请注意@WebFilter中使用的url模式

@WebFilter(filterName = "TimeOfDayFilter",
urlPatterns = {"/*"},
initParams = {
    @WebInitParam(name = "mood", value = "awake")})

它将拦截进入心情应用程序的所有请求。练习只是尝试从响应中删除图像或更改网址模式以仅拦截最终在MoodServlet中的请求

@WebFilter(filterName = "TimeOfDayFilter",
urlPatterns = {"/report"},
initParams = {
    @WebInitParam(name = "mood", value = "awake")})

两者都会导致一次调用doFilter,就像你原先预期的那样

2016-01-16T11:28:53.485+0100|Info: TimeOfDay doFilter method before sending to chain

2016-01-16T11:28:53.486+0100|Info: MoodServlet get method called

2016-01-16T11:28:53.487+0100|Info: TimeOfDay doFilter method after sending to chain

答案 4 :(得分:0)

doFilter被调用两次(或多次)时,我遇到了相同的问题。问题是过滤器处理每个请求,包括css,js,image和所有其他文件,而我希望每个页面有一个请求,因此我通过添加以下代码解决了该问题:

@WebFilter(filterName = "MyCustomFilter")
public class MyCustomFilter implements Filter {

  public void doFilter(ServletRequest request,ServletResponse response,
          FilterChain chain) throws IOException, ServletException {
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    String accept = httpRequest.getHeader("accept");

    // Since the filter handles every request
    // we have to ensure that the request is asking for text/html
    if (accept == null || !accept.toLowerCase().startsWith("text/html")) {
      chain.doFilter(request, response);
      return;
    }

    // your code goes here

希望这会帮助像我这样搜索过此问题的人。

答案 5 :(得分:0)

正如Mohan所说,如果您已经在Application类中注册了@Component,它将使您的过滤器被调用两次,

resources.add(new MyFilter());

如果是这种情况,则必须在注释或注册之间进行选择。但这仅对使用Spring的JAX-RS应用程序有效。不是这个问题的话题。

答案 6 :(得分:0)

正如@BalusC所说,在我的情况下,默认情况下浏览器正在请求favicon.ico,因此浏览器的控制台出现错误

Failed to load resource: the server responded with a status of 404 (Not Found)

所以我遵循了this answer并且错误消失了,但是随后我得到了两次被调用的过滤器。

我的解决方案

我替换了

<link rel="shortcut icon" href="#" />

为此:

<link rel="shortcut icon" href="../assets/img/valid_icon.png" />

答案 7 :(得分:-1)

在CustomFilter类中删除@Component之后,我解决了相同的问题。