Servlet 容器配置 (Servlet Config)
在 Servlet 环境中,你可以选择以编程方式配置 Servlet 容器,或者将其与 web.xml 文件结合使用。
编程方式注册 DispatcherServlet
以下示例展示了如何通过编程方式注册 DispatcherServlet:
java
import org.springframework.web.WebApplicationInitializer;
public class MyWebApplicationInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) {
XmlWebApplicationContext appContext = new XmlWebApplicationContext();
appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
ServletRegistration.Dynamic registration = container.addServlet("dispatcher", new DispatcherServlet(appContext));
registration.setLoadOnStartup(1);
registration.addMapping("/");
}
}kotlin
import org.springframework.web.WebApplicationInitializer
class MyWebApplicationInitializer : WebApplicationInitializer {
override fun onStartup(container: ServletContext) {
val appContext = XmlWebApplicationContext()
appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml")
val registration = container.addServlet("dispatcher", DispatcherServlet(appContext))
registration.setLoadOnStartup(1)
registration.addMapping("/")
}
}WebApplicationInitializer 是 Spring MVC 提供的一个接口,确保你的实现会被自动检测到,并自动用于初始化任何 Servlet 3 容器。
使用抽象基类简化配置
Spring 提供了 AbstractDispatcherServletInitializer 及其子类,使注册 DispatcherServlet 更加简单。
1. 针对 Java 配置
对于使用基于 Java 的 Spring 配置的应用,建议扩展 AbstractAnnotationConfigDispatcherServletInitializer:
java
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return null; // 根上下文配置
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { MyWebConfig.class }; // Servlet 特有配置
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" }; // 映射路径
}
}kotlin
class MyWebAppInitializer : AbstractAnnotationConfigDispatcherServletInitializer() {
override fun getRootConfigClasses(): Array<Class<*>>? {
return null
}
override fun getServletConfigClasses(): Array<Class<*>>? {
return arrayOf(MyWebConfig::class.java)
}
override fun getServletMappings(): Array<String> {
return arrayOf("/")
}
}2. 针对 XML 配置
如果你使用基于 XML 的 Spring 配置,应该直接扩展 AbstractDispatcherServletInitializer:
java
public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {
@Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
@Override
protected WebApplicationContext createServletApplicationContext() {
XmlWebApplicationContext cxt = new XmlWebApplicationContext();
cxt.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
return cxt;
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}kotlin
class MyWebAppInitializer : AbstractDispatcherServletInitializer() {
override fun createRootApplicationContext(): WebApplicationContext? {
return null
}
override fun createServletApplicationContext(): WebApplicationContext {
return XmlWebApplicationContext().apply {
setConfigLocation("/WEB-INF/spring/dispatcher-config.xml")
}
}
override fun getServletMappings(): Array<String> {
return arrayOf("/")
}
}添加过滤器 (Filters)
AbstractDispatcherServletInitializer 提供了一种便捷的方式来添加 Filter 实例,并将它们自动映射到 DispatcherServlet:
java
public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {
// ... 其他方法
@Override
protected Filter[] getServletFilters() {
return new Filter[] {
new HiddenHttpMethodFilter(), new CharacterEncodingFilter() };
}
}kotlin
class MyWebAppInitializer : AbstractDispatcherServletInitializer() {
// ... 其他方法
override fun getServletFilters(): Array<Filter> {
return arrayOf(HiddenHttpMethodFilter(), CharacterEncodingFilter())
}
}每个过滤器都会根其具体类型获得一个默认名称,并自动映射到 DispatcherServlet。
其他自定义配置
- 异步支持:重写
isAsyncSupported保护方法。默认值为true,会为DispatcherServlet及所有映射的过滤器启用异步支持。 - DispatcherServlet 实例:如果需要进一步定制
DispatcherServlet本身,可以重写createDispatcherServlet方法。
补充教学
1. 它是如何“自动检测”的?(SPI 机制)
你可能会好奇,仅仅实现一个接口,Tomcat 怎么就知道去调用它? 这是利用了 Servlet 3.0+ 的容器启动初始化机制 (SPI)。
- Spring 的
spring-webjar 包里包含一个文件:META-INF/services/javax.servlet.ServletContainerInitializer。 - 该文件指向 Spring 的
SpringServletContainerInitializer。 - 当 Tomcat 启动时,它会扫描所有实现了
WebApplicationInitializer接口的类,并将它们交给 Spring 的初始化器去执行onStartup方法。
这就是所谓的“零 XML”启动的核心原理。
2. getServletFilters 的优缺点
- 优点:极其方便。不需要去处理繁琐的
FilterRegistration。 - 缺点:灵活性受限。默认情况下,这些过滤器会被映射到
DispatcherServlet本身。如果你需要将某个过滤器映射到特定的 URL(比如只针对/admin/*),那么你可能需要回归到onStartup中手动进行addFilter操作。
3. 为什么在 Spring Boot 中看不到这些?
Spring Boot 并不依赖上述机制。
- Spring Boot 走的是“独立应用”路线:它的
main方法启动后,会自己创建一个 Tomcat 实例,然后手动把 Spring MVC 的组件塞进去。 - 因此,你在 Spring Boot 中通常通过注册
FilterRegistrationBean或ServletRegistrationBean来添加过滤器和 Servlet,而不是实现WebApplicationInitializer。