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

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的映射上下文内匹配路径,导致路径解析冲突。

解决步骤

  1. 修改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("/");
  }
}
  1. 修正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>
  1. 验证部署路径
    直接部署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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 19:22:44