You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Servlet转发JSP触发StackOverflowError,求跳过映射的方法

解决Servlet转发JSP导致的循环调用栈溢出问题

你分析的问题根源完全正确:当Servlet使用<url-pattern>/*</url-pattern>时,它的匹配优先级高于默认JSP Servlet的*.jsp扩展名匹配,所以所有请求(包括访问JSP的请求)都会先进入你的FullTracking Servlet。当你转发到/FullTracking.jsp时,这个请求又会被同一个Servlet拦截,形成无限循环,最终触发StackOverflowError。

下面给你几个可行的解决方案,按推荐程度排序:

方案1:使用Filter代替Servlet实现统计(最优解)

Servlet的映射会拦截所有请求调度类型(包括外部请求、转发、包含等),而Filter可以精准控制拦截的请求类型,完美避免循环问题。

步骤1:创建统计Filter

package com.tests;

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

public class FullTrackingFilter implements Filter {
    // 用Hashtable保证线程安全,Servlet/Filter是单例,多线程共享此对象
    private Hashtable<String, Integer> links = new Hashtable<>();

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 初始化操作(可选)
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        String uri = req.getRequestURI();
        String resource = uri.substring(req.getContextPath().length());

        // 跳过统计页面本身,避免不必要的统计
        if (!"/FullTracking.jsp".equals(resource)) {
            // 同步操作确保统计计数的原子性
            synchronized (links) {
                links.put(resource, links.getOrDefault(resource, 0) + 1);
            }
        }

        // 将统计数据放入请求,供JSP读取
        req.setAttribute("links", links);
        // 放行请求,继续执行后续的Servlet/JSP
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        // 销毁操作(可选)
    }
}

步骤2:配置Filter并指定拦截类型

在web.xml中替换原来的Servlet配置为Filter配置:

<filter>
    <filter-name>FullTrackingFilter</filter-name>
    <filter-class>com.tests.FullTrackingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>FullTrackingFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <!-- 只拦截外部发起的请求,内部转发/包含不触发Filter -->
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>

步骤3:保留原JSP无需修改

你的FullTracking.jsp可以继续使用原来的代码,访问/FullTracking.jsp就能看到所有资源的统计结果,而且不会再出现循环调用。


方案2:调整Servlet逻辑并将JSP放入WEB-INF

如果你不想改用Filter,可以通过修改Servlet逻辑+WEB-INF目录的特性来避免循环:

步骤1:移动JSP到WEB-INF目录

把FullTracking.jsp移动到项目的WEB-INF文件夹下,路径变为WEB-INF/FullTracking.jsp(WEB-INF下的资源无法通过外部URL直接访问,但服务器内部转发可以访问)。

步骤2:修改Servlet代码

在doGet方法开头添加判断,跳过WEB-INF下的资源处理,同时修改转发路径:

public class FullTracking extends HttpServlet {
    Hashtable<String,Integer> links=new Hashtable<>();

    public void doGet(HttpServletRequest req,HttpServletResponse res) throws ServletException,IOException{
        String uri=req.getRequestURI();
        String resource=uri.substring(req.getContextPath().length());

        // 跳过WEB-INF下的资源,直接交给默认Servlet/JSP处理
        if (resource.startsWith("/WEB-INF/")) {
            req.getRequestDispatcher(resource).forward(req, res);
            return;
        }

        // 原统计逻辑
        if(links.containsKey(resource)){
            Integer count=links.get(resource);
            links.put(resource,new Integer(1+count.intValue()));
        } else{
            links.put(resource,new Integer(1));
        }

        req.setAttribute("links",links);
        // 转发到WEB-INF下的JSP
        req.getRequestDispatcher("/WEB-INF/FullTracking.jsp").forward(req,res);
    }

    public void doPost(HttpServletRequest req,HttpServletResponse res) throws ServletException,IOException{
        doGet(req,res);
    }
}

步骤3:保留原Servlet映射

继续使用<url-pattern>/*</url-pattern>映射,此时访问任何资源都会被统计,转发到WEB-INF下的JSP时,Servlet会直接放行,不会触发循环。


方案3:在Servlet中跳过统计页面的处理

这个方案无需移动文件,但需要在Servlet中判断当前请求是否为统计页面,直接放行:

public void doGet(HttpServletRequest req,HttpServletResponse res) throws ServletException,IOException{
    String uri=req.getRequestURI();
    String resource=uri.substring(req.getContextPath().length());

    // 如果是统计页面,直接交给JSP Servlet处理,跳过统计逻辑
    if ("/FullTracking.jsp".equals(resource)) {
        req.getRequestDispatcher(resource).forward(req, res);
        return;
    }

    // 原统计逻辑
    if(links.containsKey(resource)){
        Integer count=links.get(resource);
        links.put(resource,new Integer(1+count.intValue()));
    } else{
        links.put(resource,new Integer(1));
    }

    req.setAttribute("links",links);
    req.getRequestDispatcher("/FullTracking.jsp").forward(req,res);
}

不过这个方案有个小问题:如果用户直接访问/FullTracking.jsp,不会被统计(这其实是合理的),但转发时的请求会被Servlet拦截一次,不过因为判断后直接放行,不会进入统计和再次转发,所以不会循环。


另外还要提醒你:你的Hashtable虽然是线程安全的,但在多线程环境下,get+put的操作不是原子性的,建议在统计逻辑上加synchronized块,避免计数错误(方案1中已经添加了同步处理)。

内容的提问来源于stack exchange,提问作者1m9K

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:36:44