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
相关产品推荐
相关产品推荐

