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

Spring Framework(无Spring Boot)无法找到JSP文件的问题排查

问题:手动搭建Spring Framework+Tomcat处理JSP失败,无法找到JSP文件

尝试不使用Spring Boot,手动实现Spring Framework处理JSP的机制,但访问接口时无法找到JSP文件。Tomcat运行正常,但日志提示No mapping for GET /hello。

原代码

public class Main {

    public static void main(String[] args) throws Exception {
        startTomcat();
    }

    static void startTomcat() throws Exception {
        Tomcat tomcat = new Tomcat();
        tomcat.getConnector().setPort(8080);
        Context context = tomcat.addContext("", System.getProperty("java.io.tmpdir"));

        AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
        appContext.register(SpringConfig.class);
        appContext.refresh();

        DispatcherServlet dispatcherServlet = new DispatcherServlet(appContext);
        Wrapper wrapper = context.createWrapper();
        wrapper.setName("dispatcherServlet");
        wrapper.setServlet(dispatcherServlet);
        context.addChild(wrapper);
        wrapper.setLoadOnStartup(1);
        wrapper.addMapping("/");

        try {
            tomcat.start();
            tomcat.getServer().await();
        } catch (LifecycleException e) {
            System.out.println("Katakroker");
        }
    }

    @Configuration
    class SpringConfig {

        @Bean
        static StaticWebApplicationContext applicationContext() {
            System.out.println("Adding a StaticWebApplicationContext");
            StaticWebApplicationContext applicationContext = new StaticWebApplicationContext();
            // Add JSP View Resolver
            applicationContext.registerBean(ViewResolver.class, () -> {
                InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
                viewResolver.setViewClass(JstlView.class);
                viewResolver.setPrefix("/WEB-INF/"); // Path to JSP files
                viewResolver.setSuffix(".jsp");
                return viewResolver;
            });
            return applicationContext;
        }

        @Bean
        RouterFunction<ServerResponse> routes() {
            System.out.println("Adding a route");
            return RouterFunctions.route()
                    .GET("/hello2", request -> ServerResponse.ok().render("hello")) // Handle JSP rendering
                    .build();
        }

        @PostConstruct
        private void postConstruct() {
            System.out.println("Running Spring Config");
        }
    }

}

执行日志

Running Spring Config
Adding a StaticWebApplicationContext
Adding a route
Aug 28, 2023 4:39:44 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-nio-8080"]
Aug 28, 2023 4:39:44 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service [Tomcat]
Aug 28, 2023 4:39:44 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet engine: [Apache Tomcat/10.1.11]
Aug 28, 2023 4:39:44 PM org.apache.catalina.core.ApplicationContext log
INFO: Initializing Spring DispatcherServlet 'dispatcherServlet'
16:39:44.395 [info.jab.ms.jsp.Main.main()] INFO org.springframework.web.servlet.DispatcherServlet -- Initializing Servlet 'dispatcherServlet'
16:39:44.669 [info.jab.ms.jsp.Main.main()] INFO org.springframework.web.servlet.DispatcherServlet -- Completed initialization in 269 ms
Aug 28, 2023 4:39:44 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-nio-8080"]
16:40:11.225 [http-nio-8080-exec-2] WARN org.springframework.web.servlet.PageNotFound -- No mapping for GET /hello

问题分析与修复方案

1. 核心问题点

  • 嵌套上下文未生效:SpringConfig中创建的StaticWebApplicationContext是独立的子上下文,并未被外层的AnnotationConfigWebApplicationContext整合,导致注册的ViewResolver完全没被DispatcherServlet加载。
  • Tomcat资源路径错误:将Tomcat的Web根目录设为系统临时目录(java.io.tmpdir),项目中WEB-INF下的JSP文件不在该路径下,Tomcat无法找到。
  • WebFlux与MVC混用:RouterFunction和ServerResponse.render()是Spring WebFlux的API,与Spring MVC的DispatcherServlet不兼容,无法正确触发JSP视图渲染。
  • 非静态内部类无法被加载:SpringConfig是非静态内部类,AnnotationConfigWebApplicationContext无法直接实例化它。

2. 修正后的代码

public class Main {

    public static void main(String[] args) throws Exception {
        startTomcat();
    }

    static void startTomcat() throws Exception {
        Tomcat tomcat = new Tomcat();
        tomcat.getConnector().setPort(8080);
        // 指定Maven项目标准Web资源根目录
        String webAppDir = new File("src/main/webapp").getAbsolutePath();
        Context context = tomcat.addContext("", webAppDir);

        AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
        appContext.register(SpringConfig.class);
        appContext.refresh();

        DispatcherServlet dispatcherServlet = new DispatcherServlet(appContext);
        Wrapper wrapper = context.createWrapper();
        wrapper.setName("dispatcherServlet");
        wrapper.setServlet(dispatcherServlet);
        context.addChild(wrapper);
        wrapper.setLoadOnStartup(1);
        wrapper.addMapping("/");

        // 手动注册JSP Servlet(嵌入式Tomcat默认未自动注册)
        Wrapper jspWrapper = context.createWrapper();
        jspWrapper.setName("jsp");
        jspWrapper.setServletClass("org.apache.jasper.servlet.JspServlet");
        jspWrapper.addMapping("*.jsp");
        context.addChild(jspWrapper);

        try {
            tomcat.start();
            tomcat.getServer().await();
        } catch (LifecycleException e) {
            e.printStackTrace();
        }
    }

    @Configuration
    static class SpringConfig { // 修改为静态内部类,允许被外部上下文加载

        @Bean
        ViewResolver viewResolver() {
            // 直接在当前上下文注册ViewResolver
            InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
            viewResolver.setViewClass(JstlView.class);
            viewResolver.setPrefix("/WEB-INF/");
            viewResolver.setSuffix(".jsp");
            return viewResolver;
        }

        @PostConstruct
        private void postConstruct() {
            System.out.println("Running Spring Config");
        }
    }

    // 使用Spring MVC标准Controller处理请求
    @Controller
    static class HelloController {
        @GetMapping("/hello2")
        public String hello(Model model) {
            model.addAttribute("message", "Hello from JSP!");
            return "hello"; // 对应WEB-INF/hello.jsp
        }
    }
}

3. 补充JSP文件

在项目src/main/webapp/WEB-INF目录下创建hello.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Hello JSP</title>
</head>
<body>
    <h1>${message}</h1>
</body>
</html>

4. 验证

运行Main类后,访问http://localhost:8080/hello2即可正常渲染JSP页面。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 01:13:09