Skip to content

Spring MVC Default Servlet 配置详解 🚀

概述

在 Spring MVC 应用中,我们经常遇到这样的需求:既要让 DispatcherServlet 处理所有的 Web 请求,又要能正常访问静态资源(如 CSS、JS、图片等)。Spring MVC 的 Default Servlet 配置正是为了解决这个看似矛盾的问题而设计的。

IMPORTANT

Default Servlet 配置允许 Spring MVC 的 DispatcherServlet 映射到根路径 /,同时仍然能够正确处理静态资源请求。

核心问题与解决方案 🎯

问题背景

在传统的 Web 应用中,我们面临着一个经典的冲突:

kotlin
// 我们希望 DispatcherServlet 处理所有请求
@Configuration
class WebConfig {
    // DispatcherServlet 映射到 "/"
    // 但这会覆盖容器的默认 Servlet
    // 导致静态资源无法访问
}
kotlin
// 我们希望达到的效果
class IdealScenario {
    // ✅ API 请求: /api/users -> Spring Controller 处理
    // ✅ 静态资源: /css/style.css -> 容器默认 Servlet 处理
    // ✅ 静态资源: /js/app.js -> 容器默认 Servlet 处理
}

解决方案原理

Spring MVC 通过 DefaultServletHttpRequestHandler 巧妙地解决了这个问题:

NOTE

DefaultServletHttpRequestHandler 具有最低的优先级(Integer.MAX_VALUE),确保只有在没有其他 Handler 能处理请求时才会被调用。

基础配置 ⚙️

启用 Default Servlet 处理

最简单的配置方式:

kotlin
@Configuration
class WebConfiguration : WebMvcConfigurer {

    override fun configureDefaultServletHandling(configurer: DefaultServletHandlerConfigurer) {
        configurer.enable() 
    }
}

TIP

调用 configurer.enable() 后,Spring 会自动创建一个 DefaultServletHttpRequestHandler,并将其映射到 /** 路径。

工作机制详解

让我们通过一个完整的示例来理解其工作原理:

kotlin
@RestController
@RequestMapping("/api")
class UserController {
    
    @GetMapping("/users")
    fun getUsers(): List<String> {
        return listOf("Alice", "Bob", "Charlie")
    }
    
    @GetMapping("/users/{id}")
    fun getUser(@PathVariable id: String): String {
        return "User: $id"
    }
}
kotlin
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
    
    override fun configureDefaultServletHandling(configurer: DefaultServletHandlerConfigurer) {
        configurer.enable()
        // 这会创建一个 DefaultServletHttpRequestHandler
        // 映射到 /** 并设置最低优先级
    }
    
    // 其他配置...
}

请求处理流程

高级配置 🔧

自定义默认 Servlet 名称

在某些情况下,你可能需要指定自定义的默认 Servlet 名称:

kotlin
@Configuration
class CustomDefaultServletConfiguration : WebMvcConfigurer {

    override fun configureDefaultServletHandling(configurer: DefaultServletHandlerConfigurer) {
        // 指定自定义的默认 Servlet 名称
        configurer.enable("myCustomDefaultServlet") 
    }
}

WARNING

只有在以下情况下才需要指定自定义名称:

  • 默认 Servlet 被重新配置了不同的名称
  • 使用了 Spring 不认识的 Servlet 容器

支持的容器与默认名称

Spring MVC 自动检测以下主流容器的默认 Servlet:

容器默认 Servlet 名称
Tomcatdefault
Jettydefault
GlassFishdefault
JBossdefault
WebLogicFileServlet
WebSphereSimpleFileServlet

INFO

如果你使用的是上述容器之一,通常不需要手动指定 Servlet 名称,Spring 会自动检测。

实际应用场景 🌟

场景一:前后端分离项目

在前后端分离的项目中,后端提供 API,前端构建后的静态文件也需要被服务:

kotlin
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
    
    override fun configureDefaultServletHandling(configurer: DefaultServletHandlerConfigurer) {
        configurer.enable()
    }
    
    @RestController
    @RequestMapping("/api")
    class ApiController {
        
        @GetMapping("/health")
        fun health(): Map<String, String> {
            return mapOf("status" to "UP")
        }
    }
}

这样配置后:

  • /api/health → Spring Controller 处理
  • /index.html → 默认 Servlet 处理
  • /static/css/app.css → 默认 Servlet 处理
  • /static/js/app.js → 默认 Servlet 处理

场景二:与静态资源处理的配合

Default Servlet 配置可以与 Spring 的静态资源处理配合使用:

kotlin
@Configuration
class ResourceConfig : WebMvcConfigurer {
    
    override fun configureDefaultServletHandling(configurer: DefaultServletHandlerConfigurer) {
        configurer.enable()
    }
    
    override fun addResourceHandlers(registry: ResourceHandlerRegistry) {
        // 优先级高于 Default Servlet Handler
        registry.addResourceHandler("/assets/**") 
            .addResourceLocations("classpath:/static/assets/")
            .setCachePeriod(3600)
    }
}

TIP

在这种配置下,/assets/** 路径的请求会被 Spring 的资源处理器处理(支持缓存、版本控制等高级功能),而其他静态资源请求则由默认 Servlet 处理。

注意事项与最佳实践 ⚠️

优先级考虑

IMPORTANT

DefaultServletHttpRequestHandler 必须保持最低优先级,确保它是最后的处理选择。

kotlin
@Configuration
class HandlerMappingConfig : WebMvcConfigurer {
    
    @Bean
    fun customHandlerMapping(): HandlerMapping {
        val mapping = SimpleUrlHandlerMapping()
        mapping.urlMap = mapOf("/custom/**" to customHandler())
        // 确保优先级高于 DefaultServletHttpRequestHandler
        mapping.order = 1
        return mapping
    }
    
    override fun configureDefaultServletHandling(configurer: DefaultServletHandlerConfigurer) {
        configurer.enable()
        // 这会设置优先级为 Integer.MAX_VALUE
    }
}

性能考虑

性能提示

虽然 Default Servlet 配置很方便,但对于生产环境,建议:

  1. 使用专门的 Web 服务器(如 Nginx)处理静态资源
  2. 或者使用 Spring 的静态资源处理器,它提供了更多优化功能

调试技巧

当遇到静态资源访问问题时,可以启用调试日志:

kotlin
// application.yml
logging:
  level:
    org.springframework.web.servlet.handler.SimpleUrlHandlerMapping: DEBUG
    org.springframework.web.servlet.resource: DEBUG

总结 📝

Spring MVC 的 Default Servlet 配置是一个优雅的解决方案,它解决了 DispatcherServlet 全路径映射与静态资源访问之间的冲突。通过理解其工作原理和正确配置,我们可以构建既能处理动态请求又能服务静态资源的 Web 应用。

核心要点回顾

  1. 问题解决:允许 DispatcherServlet 映射到 / 的同时处理静态资源
  2. 工作原理:通过最低优先级的 Handler 转发请求到容器默认 Servlet
  3. 配置简单:只需调用 configurer.enable() 即可
  4. 自动检测:支持主流容器的自动检测,无需手动配置

记住,这只是静态资源处理的一种方案,在实际生产环境中,还需要根据具体需求选择最合适的策略! 🎉