Appearance
Spring MVC Servlet 配置详解 ⚙️
引言:告别 web.xml 的时代 👋
在传统的 Java Web 开发中,我们总是需要在 web.xml
文件中配置各种 Servlet、Filter 和监听器。这种 XML 配置方式虽然功能强大,但也带来了一些问题:
- 配置繁琐:需要编写大量的 XML 配置代码
- 类型不安全:XML 配置在编译时无法检查错误
- 维护困难:配置分散在多个文件中,难以统一管理
Spring MVC 为我们提供了一种更现代、更优雅的解决方案:编程式 Servlet 配置。
TIP
编程式配置让我们可以用 Java/Kotlin 代码来替代传统的 XML 配置,享受类型安全、IDE 智能提示等现代开发体验。
核心概念:WebApplicationInitializer 接口 💡
什么是 WebApplicationInitializer?
WebApplicationInitializer
是 Spring MVC 提供的一个核心接口,它的作用是:
- 自动发现:Spring 会自动检测实现了此接口的类
- 容器初始化:在 Servlet 3.0+ 容器启动时自动执行初始化逻辑
- 编程式配置:允许我们用代码来配置 DispatcherServlet
基础配置:手动注册 DispatcherServlet 🛠️
最原始的配置方式
让我们先看看最基础的配置方式:
kotlin
import org.springframework.web.WebApplicationInitializer
import org.springframework.web.context.support.XmlWebApplicationContext
import org.springframework.web.servlet.DispatcherServlet
import javax.servlet.ServletContext
class MyWebApplicationInitializer : WebApplicationInitializer {
override fun onStartup(container: ServletContext) {
// 创建Spring应用上下文
val appContext = XmlWebApplicationContext()
appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml")
// 注册DispatcherServlet
val registration = container.addServlet("dispatcher", DispatcherServlet(appContext))
registration.setLoadOnStartup(1) // 容器启动时立即加载
registration.addMapping("/") // 处理所有请求
}
}
xml
<!-- /WEB-INF/spring/dispatcher-config.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="...">
<mvc:annotation-driven />
<context:component-scan base-package="com.example.controller" />
</beans>
NOTE
这种方式虽然灵活,但需要我们手动处理很多细节。Spring 提供了更简便的抽象类来简化这个过程。
进阶配置:使用抽象基类 🚀
基于注解的配置(推荐)
对于现代 Spring Boot 应用,我们更推荐使用基于 Java 配置的方式:
kotlin
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer
class MyWebAppInitializer : AbstractAnnotationConfigDispatcherServletInitializer() {
// 根应用上下文配置类(通常用于服务层、数据层等)
override fun getRootConfigClasses(): Array<Class<*>>? {
return null // 在简单应用中可以返回null
}
// Servlet应用上下文配置类(用于Web层)
override fun getServletConfigClasses(): Array<Class<*>>? {
return arrayOf(MyWebConfig::class.java)
}
// Servlet映射路径
override fun getServletMappings(): Array<String> {
return arrayOf("/") // 处理所有请求
}
}
对应的配置类:
kotlin
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.EnableWebMvc
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
@Configuration
@EnableWebMvc // 启用Spring MVC
@ComponentScan("com.example.controller") // 扫描控制器
class MyWebConfig : WebMvcConfigurer {
// 可以在这里添加更多的Web配置
// 比如视图解析器、拦截器、消息转换器等
}
基于 XML 的配置
如果你的项目仍然使用 XML 配置,可以这样做:
XML配置方式示例
kotlin
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer
import org.springframework.web.context.WebApplicationContext
import org.springframework.web.context.support.XmlWebApplicationContext
class MyWebAppInitializer : AbstractDispatcherServletInitializer() {
// 创建根应用上下文
override fun createRootApplicationContext(): WebApplicationContext? {
return null
}
// 创建Servlet应用上下文
override fun createServletApplicationContext(): WebApplicationContext {
return XmlWebApplicationContext().apply {
setConfigLocation("/WEB-INF/spring/dispatcher-config.xml")
}
}
// 配置Servlet映射
override fun getServletMappings(): Array<String> {
return arrayOf("/")
}
}
高级特性:Filter 配置 🛡️
自动配置过滤器
Spring MVC 还允许我们轻松地添加过滤器:
kotlin
import org.springframework.web.filter.CharacterEncodingFilter
import org.springframework.web.filter.HiddenHttpMethodFilter
import javax.servlet.Filter
class MyWebAppInitializer : AbstractDispatcherServletInitializer() {
// ... 其他配置方法 ...
// 配置过滤器
override fun getServletFilters(): Array<Filter> {
return arrayOf(
HiddenHttpMethodFilter(), // 支持PUT、DELETE等HTTP方法
CharacterEncodingFilter().apply { // 字符编码过滤器
setEncoding("UTF-8")
setForceEncoding(true)
}
)
}
}
过滤器的工作原理
IMPORTANT
过滤器会按照数组中的顺序执行,请求时正序执行,响应时逆序执行。
异步支持配置 ⚡
启用异步处理
现代 Web 应用经常需要处理异步请求,Spring MVC 默认启用了异步支持:
kotlin
class MyWebAppInitializer : AbstractDispatcherServletInitializer() {
// ... 其他配置 ...
// 异步支持(默认为true)
override fun isAsyncSupported(): Boolean {
return true
}
}
异步处理的应用场景
异步处理的优势
- 提高吞吐量:释放线程资源,处理更多并发请求
- 长时间操作:适合文件上传、数据导出等耗时操作
- 实时通信:支持 Server-Sent Events、WebSocket 等技术
自定义 DispatcherServlet 🔧
高级定制
如果需要对 DispatcherServlet 进行更深层次的定制:
kotlin
import org.springframework.web.servlet.DispatcherServlet
class MyWebAppInitializer : AbstractDispatcherServletInitializer() {
// ... 其他配置 ...
// 自定义DispatcherServlet
override fun createDispatcherServlet(servletAppContext: WebApplicationContext): DispatcherServlet {
return object : DispatcherServlet(servletAppContext) {
override fun onRefresh(context: ApplicationContext) {
super.onRefresh(context)
// 在这里可以添加自定义的初始化逻辑
println("DispatcherServlet 初始化完成!")
}
}
}
}
实际应用示例 💻
完整的 Spring Boot 风格配置
让我们看一个更贴近实际项目的完整示例:
kotlin
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer
class WebAppInitializer : AbstractAnnotationConfigDispatcherServletInitializer() {
override fun getRootConfigClasses(): Array<Class<*>>? {
return arrayOf(RootConfig::class.java) // 根配置
}
override fun getServletConfigClasses(): Array<Class<*>>? {
return arrayOf(WebConfig::class.java) // Web配置
}
override fun getServletMappings(): Array<String> {
return arrayOf("/api/*") // 只处理API请求
}
override fun getServletFilters(): Array<Filter> {
return arrayOf(
CharacterEncodingFilter().apply {
setEncoding("UTF-8")
setForceEncoding(true)
},
CorsFilter() // 跨域支持
)
}
}
kotlin
@Configuration
@EnableWebMvc
@ComponentScan("com.example.controller")
class WebConfig : WebMvcConfigurer {
// 配置消息转换器
override fun configureMessageConverters(converters: MutableList<HttpMessageConverter<*>>) {
converters.add(MappingJackson2HttpMessageConverter())
}
// 配置跨域
override fun addCorsMappings(registry: CorsRegistry) {
registry.addMapping("/api/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
}
}
kotlin
@Configuration
@ComponentScan(
basePackages = ["com.example.service", "com.example.repository"],
excludeFilters = [ComponentScan.Filter(type = FilterType.ANNOTATION, classes = [Controller::class])]
)
class RootConfig {
// 服务层和数据层的配置
}
配置方式对比 ⚖️
传统 vs 现代配置
xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="3.0">
<!-- 字符编码过滤器 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<!-- DispatcherServlet配置 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/dispatcher-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
kotlin
class WebAppInitializer : AbstractAnnotationConfigDispatcherServletInitializer() {
override fun getRootConfigClasses() = null
override fun getServletConfigClasses() = arrayOf(WebConfig::class.java)
override fun getServletMappings() = arrayOf("/")
override fun getServletFilters() = arrayOf(
CharacterEncodingFilter().apply {
setEncoding("UTF-8")
setForceEncoding(true)
}
)
}
NOTE
编程式配置的优势:
- ✅ 类型安全:编译时检查错误
- ✅ IDE 支持:智能提示和重构
- ✅ 代码复用:可以继承和组合
- ✅ 动态配置:可以根据环境动态调整
最佳实践建议 ⭐
1. 选择合适的配置方式
kotlin
// 推荐:对于新项目使用注解配置
class ModernWebInitializer : AbstractAnnotationConfigDispatcherServletInitializer() {
// 基于Java配置类
}
// 兼容:对于遗留项目可以混合使用
class LegacyWebInitializer : AbstractDispatcherServletInitializer() {
// 基于XML配置文件
}
2. 合理组织配置类
kotlin
// 根配置:业务逻辑相关
@Configuration
@ComponentScan(basePackages = ["com.example.service", "com.example.repository"])
class RootConfig
// Web配置:Web层相关
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = ["com.example.controller"])
class WebConfig : WebMvcConfigurer
3. 过滤器顺序很重要
kotlin
override fun getServletFilters(): Array<Filter> {
return arrayOf(
CharacterEncodingFilter(), // 1. 首先设置字符编码
CorsFilter(), // 2. 然后处理跨域
HiddenHttpMethodFilter(), // 3. 最后处理HTTP方法
)
}
WARNING
过滤器的执行顺序会影响应用的行为,请确保按照正确的顺序配置。
总结 🎉
Spring MVC 的编程式 Servlet 配置为我们提供了一种现代化、类型安全的配置方式。通过 WebApplicationInitializer
接口和相关的抽象类,我们可以:
- 告别 XML:用 Java/Kotlin 代码替代繁琐的 XML 配置
- 类型安全:享受编译时检查和 IDE 智能提示
- 灵活配置:轻松添加过滤器、自定义 Servlet 等
- 现代化开发:与 Spring Boot 等现代框架完美集成
选择合适的配置方式,让你的 Spring MVC 应用更加优雅和易于维护! ✨