为什么我的Servlet过滤器会运行两次?

isa*_*pir 1 servlets intellij-idea java-ee embedded-jetty servlet-filters

我有一个简单的Servlet过滤器:

package net.twentyonesolutions;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Date;

public class TestFilter implements Filter {

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println(TestFilter.class.getSimpleName()       // line 12
                + " > "
                + ((HttpServletRequest)servletRequest).getRequestURL()
                + ": IP "+ servletRequest.getRemoteAddr()
                + "; at "
                + new Date().toString());

        // Pass request back down the filter chain
        filterChain.doFilter(servletRequest, servletResponse);    // line 20
    }

    public void init(FilterConfig filterConfig) throws ServletException {}

    public void destroy() {}
}
Run Code Online (Sandbox Code Playgroud)

我把它添加到web.xml中,如下所示:

<filter>
    <filter-name>TestFilter</filter-name>
    <filter-class>net.twentyonesolutions.TestFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>TestFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
Run Code Online (Sandbox Code Playgroud)

我在IntelliJ IDEA的调试模式下使用嵌入式Jetty运行它.我希望第12行的输出写入一次,但是它被写入两次,并且两次都在Servlet被调用之前.

为什么叫两次?应该被叫两次吗?如果是这样,我怎么能从第二次第一次告诉,因为我希望我的代码只运行一次.

我发现了一些关于这个问题的老问题,但没有一个有真正的解决方案或解释.

您可以在下面的屏幕截图中看到第一个调用和第二个调用之间的堆栈跟踪差异:

在此输入图像描述

UPDATE

当我将过滤器部署到常规(非嵌入式)Jetty(与嵌入式版本相同的版本9.2.18.v20160721)或Tomcat时 - doFilter()每个请求只执行一次,如预期的那样,因此问题在于使用IntelliJ IDEA中的调试或使用嵌入式Jetty.

更新嵌入代码:

    WebAppContext webapp = new WebAppContext();
    webapp.setContextPath("/");
    webapp.setResourceBase(webRoot);
    webapp.setDefaultsDescriptor(webRoot + "/WEB-INF/web.xml");     

    Server server = new Server();

    ServerConnector http = new ServerConnector(server);
    http.setPort(port);             
    if (!host.equals(""))
        http.setHost(host);

    server.setConnectors(new ServerConnector[] { http });
    server.setHandler(webapp);      
    server.start();
    server.join();
Run Code Online (Sandbox Code Playgroud)

更新filterChain.toString()

我在调用之前添加了filterChain.toString()的打印,filterChain.doFilter()它清楚地显示了差异.

filterChain(第一次调用之前)中的第一个对象doFilter()是我的过滤器.看两条印刷线:

TestFilter > fdvnqp1k0ut37fenee569vshqf http://localhost:8080/index.cfm: IP 0:0:0:0:0:0:0:1; at Tue Sep 06 09:59:09 PDT 2016
    ... filterChain: TestFilter->CFMLServlet@cdad6a3==lucee.debug.loader.servlet.CFMLServlet,1,true
TestFilter > fdvnqp1k0ut37fenee569vshqf http://localhost:8080/index.cfm: IP 0:0:0:0:0:0:0:1; at Tue Sep 06 09:59:09 PDT 2016
    ... filterChain: CFMLServlet@cdad6a3==lucee.debug.loader.servlet.CFMLServlet,1,true
Run Code Online (Sandbox Code Playgroud)

我不确定规格,但我希望在第一次调用TestFilter之前将其从链中移除TestFilter.doFilter(),不是吗?

gre*_*egw 5

我认为问题在于您将WEB-INF/web.xml设置为默认描述符.因此web.xml正在运行两次 - 一次设置默认值,一次设置为发现的WEB-INF/web.xml - 因此您将获得两个过滤器实例.

不要设置默认描述符(或者如果你想知道默认值,则将其设置为null - 但是你不会有默认的servlet).