Tomcat 10.x部署Spring MVC应用控制器调用失败问题
Spring MVC在Tomcat 10.1.x中404异常排查与解决
问题现象
在Jetty环境执行mvn clean install jetty:run后,访问http://localhost:8080/hello可正常响应;但在Tomcat 10.1.8/10.1.9中,无论是通过mvn clean package cargo:run运行cargo插件,还是直接部署war包,访问同一URL始终返回404错误,日志显示"GET /hello HTTP/1.1" 404及"No mapping for GET /hello",控制器已被识别但映射无效。当前环境:JDK 17、Spring Framework 6。
相关代码与配置
1. Controller代码
package test; import java.util.Locale; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; @Controller public class HelloController { @GetMapping("/hello") public String homeInit(Locale locale, Model model) { return "hello"; } }
2. 应用初始化类
package test; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletRegistration; import org.springframework.web.WebApplicationInitializer; import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; public class MyApplicationInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) { AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.setConfigLocation("test.WebMvcConfig"); servletContext.addListener(new ContextLoaderListener(context)); ServletRegistration.Dynamic dispatcher = servletContext .addServlet("hello", new DispatcherServlet(context)); dispatcher.setLoadOnStartup(2); dispatcher.addMapping("/hello"); } }
3. MVC配置类
package test; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.validation.Validator; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.view.InternalResourceViewResolver; import org.springframework.web.servlet.view.JstlView; @Configuration @EnableWebMvc @ComponentScan(basePackages = {"test"}) public class WebMvcConfig implements WebMvcConfigurer { @Bean public InternalResourceViewResolver resolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setViewClass(JstlView.class); resolver.setPrefix("/WEB-INF/views/"); resolver.setSuffix(".jsp"); return resolver; } }
4. JSP视图文件
<html> <head><title>Spring Web MVC Example</title></head> <body> <h1>Test</h1> <h2>Spring Web MVC Example</h2> </body> </html>
5. pom.xml配置
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>test</groupId> <artifactId>spring-webmvc-test</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>spring-webmvc Maven Webapp</name> <url>http://maven.apache.org</url> <properties> <java.version>17</java.version> <failOnMissingWebXml>false</failOnMissingWebXml> <spring.version>6.0.6</spring.version> </properties> <dependencies> <!-- Spring MVC Dependency --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> <version>6.0.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>jakarta.servlet.jsp</groupId> <artifactId>jakarta.servlet.jsp-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.glassfish.web</groupId> <artifactId>jakarta.servlet.jsp.jstl</artifactId> <version>3.0.1</version> </dependency> <dependency> <groupId>jakarta.servlet.jsp.jstl</groupId> <artifactId>jakarta.servlet.jsp.jstl-api</artifactId> <version>3.0.0</version> </dependency> <dependency> <groupId>jakarta.el</groupId> <artifactId>jakarta.el-api</artifactId> <version>5.0.0</version> <scope>provided</scope> </dependency> </dependencies> <build> <sourceDirectory>src/main/java</sourceDirectory> <resources> <resource> <directory>src/main/resources</directory> </resource> </resources> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.11.0</version> <configuration> <source>17</source> <target>17</target> </configuration> </plugin> <plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>11.0.15</version> </plugin> <plugin> <groupId>org.codehaus.cargo</groupId> <artifactId>cargo-maven3-plugin</artifactId> <configuration> <container> <containerId>tomcat10x</containerId> <artifactInstaller> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat</artifactId> <version>10.0.27</version> </artifactInstaller> </container> <configuration> <properties> <!-- <cargo.servlet.port>8080</cargo.servlet.port> --> <cargo.logging>low</cargo.logging> </properties> </configuration> </configuration> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>3.3.2</version> </plugin> </plugins> </build> </project>
问题原因与解决方法
核心问题:DispatcherServlet映射路径配置错误
当前MyApplicationInitializer中,DispatcherServlet的映射路径设置为/hello,这意味着只有请求路径完全匹配/hello时才会被DispatcherServlet处理,但Spring MVC控制器的@GetMapping("/hello")会尝试在DispatcherServlet的映射上下文内匹配路径,导致路径解析冲突。
解决步骤
- 修改DispatcherServlet映射路径
将dispatcher.addMapping("/hello");改为dispatcher.addMapping("/");,让DispatcherServlet处理所有请求(静态资源可后续通过Spring配置排除):
public class MyApplicationInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext servletContext) { AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.setConfigLocation("test.WebMvcConfig"); servletContext.addListener(new ContextLoaderListener(context)); ServletRegistration.Dynamic dispatcher = servletContext .addServlet("hello", new DispatcherServlet(context)); dispatcher.setLoadOnStartup(2); // 修改为处理所有请求 dispatcher.addMapping("/"); } }
- 修正Cargo插件的Tomcat版本
当前Cargo插件配置的Tomcat版本是10.0.27,与你实际使用的10.1.x版本不匹配,可能引发兼容性问题。更新Cargo配置中的Tomcat版本:
<plugin> <groupId>org.codehaus.cargo</groupId> <artifactId>cargo-maven3-plugin</artifactId> <configuration> <container> <containerId>tomcat10x</containerId> <artifactInstaller> <groupId>org.apache.tomcat</groupId> <artifactId>tomcat</artifactId> <!-- 改为你使用的10.1.x版本,比如10.1.9 --> <version>10.1.9</version> </artifactInstaller> </container> <configuration> <properties> <cargo.logging>low</cargo.logging> </properties> </configuration> </configuration> </plugin>
- 验证部署路径
直接部署war包时,Tomcat会将war包名称作为上下文路径,比如war包名为spring-webmvc-test-0.0.1-SNAPSHOT.war,访问路径应为http://localhost:8080/spring-webmvc-test-0.0.1-SNAPSHOT/hello,而非http://localhost:8080/hello。如果需要根路径访问,可将war包重命名为ROOT.war。
额外检查点
- 确认Tomcat的webapps目录下的war包已正确解压,无文件损坏
- 查看Tomcat日志中的Spring启动日志,确认
HelloController被正确扫描并注册
内容的提问来源于stack exchange,提问作者Eisberg
相关产品推荐
相关产品推荐

