Skip to content

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)

  1. Spring 的 spring-web jar 包里包含一个文件:META-INF/services/javax.servlet.ServletContainerInitializer
  2. 该文件指向 Spring 的 SpringServletContainerInitializer
  3. 当 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 中通常通过注册 FilterRegistrationBeanServletRegistrationBean 来添加过滤器和 Servlet,而不是实现 WebApplicationInitializer

Based on Spring Framework.