SpringMVC容器初始化与DispatcherServlet
SpringMVC采用父子容器架构,WebApplicationContext作为子容器继承根容器配置。
父子容器架构
XML
Root WebApplicationContext (Spring容器)
- Service、Repository、DataSource等
- ContextLoaderListener创建
↓
Servlet WebApplicationContext (SpringMVC容器)
- Controller、HandlerMapping、HandlerAdapter等
- DispatcherServlet创建
ContextLoaderListener初始化
配置方式
Java
<!-- web.xml -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.example.config.RootConfig</param-value>
</context-param>
源码解析
Java
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
// 初始化根WebApplicationContext
initWebApplicationContext(event.getServletContext());
}
}
public class ContextLoader {
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
// 检查是否已存在根容器
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException("已存在根容器");
}
try {
// 创建根容器
WebApplicationContext context = createWebApplicationContext(servletContext);
// 配置并刷新
configureAndRefreshWebApplicationContext(context, servletContext);
// 存入ServletContext
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
return context;
} catch (RuntimeException ex) {
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
}
}
DispatcherServlet容器创建
FrameworkServlet.initWebApplicationContext()
Java
protected WebApplicationContext initWebApplicationContext() {
// 获取根容器
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
// 1. 如果已注入WebApplicationContext
if (this.webApplicationContext != null) {
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
if (!((ConfigurableWebApplicationContext) wac).isActive()) {
((ConfigurableWebApplicationContext) wac).setParent(rootContext);
configureAndRefreshWebApplicationContext((ConfigurableWebApplicationContext) wac);
}
}
}
// 2. 查找已存在的WebApplicationContext
if (wac == null) {
wac = findWebApplicationContext();
}
// 3. 创建新的WebApplicationContext
if (wac == null) {
wac = createWebApplicationContext(rootContext);
}
// 4. 触发onRefresh
if (!this.refreshEventReceived) {
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
}
return wac;
}
createWebApplicationContext()
Java
protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
Class<?> contextClass = getContextClass();
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Invalid context class");
}
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
wac.setParent(parent); // 设置父容器
wac.setServletContext(getServletContext());
String configLocation = getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
configureAndRefreshWebApplicationContext(wac);
return wac;
}
父子容器Bean访问
Java
// 子容器可访问父容器Bean,父容器不可访问子容器Bean
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
protected <T> T doGetBean(String name, Class<T> requiredType,
Object[] args, boolean typeCheckOnly) {
// 1. 先检查本容器
Object bean = getSingleton(name);
if (bean != null) {
return (T) bean;
}
// 2. 检查父容器
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null) {
if (args != null) {
return (T) parentBeanFactory.getBean(name, args);
} else {
return (T) parentBeanFactory.getBean(name, requiredType);
}
}
// 3. 创建Bean
return createBean(name, requiredType, args);
}
}
Spring Boot自动配置
DispatcherServletAutoConfiguration
Java
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
public class DispatcherServletAutoConfiguration {
@Configuration
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(Servlet.class)
protected static class DispatcherServletConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
@ConditionalOnMissingBean
public DispatcherServlet dispatcherServlet() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(true);
dispatcherServlet.setDispatchTraceRequest(true);
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
return dispatcherServlet;
}
}
@Configuration
@Conditional(ServletRegistrationCondition.class)
@ConditionalOnClass(Servlet.class)
@EnableConfigurationProperties(WebMvcProperties.class)
protected static class DispatcherServletRegistrationConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnMissingBean(value = DispatcherServlet.class, ignored = DispatcherServlet.class)
public DispatcherServletRegistrationBean dispatcherServletRegistration(
WebMvcProperties webMvcProperties, DispatcherServlet dispatcherServlet) {
DispatcherServletRegistrationBean registration =
new DispatcherServletRegistrationBean(dispatcherServlet, webMvcProperties.getServlet().getPath());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
return registration;
}
}
}
ServletWebServerApplicationContext
Java
public class ServletWebServerApplicationContext extends GenericWebApplicationContext
implements WebServerApplicationContext {
private volatile WebServer webServer;
@Override
public void refresh() {
try {
// 创建WebServer(内嵌Tomcat/Jetty等)
createWebServer();
super.refresh();
// 启动WebServer
startWebServer();
} catch (Exception ex) {
stopAndReleaseWebServer();
throw ex;
}
}
private void createWebServer() {
ServletWebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getServletContextInitializer());
}
}
WebApplicationContext类型
| 类型 | 说明 | 适用场景 |
|---|---|---|
| XmlWebApplicationContext | XML配置 | 传统XML配置 |
| AnnotationConfigWebApplicationContext | 注解配置 | 注解驱动开发 |
| GroovyWebApplicationContext | Groovy配置 | Groovy DSL配置 |
DispatcherServlet配置属性
Java
// FrameworkServlet关键属性
public class FrameworkServlet extends HttpServletBean {
private String contextClass; // WebApplicationContext类型
private String contextConfigLocation; // 配置位置
private WebApplicationContext webApplicationContext; // 上下文实例
private boolean publishContext = true; // 是否发布到ServletContext
private boolean publishEvents = true; // 是否发布事件
}
多DispatcherServlet配置
text
@Configuration
public class MultiServletConfig {
@Bean
public DispatcherServlet apiDispatcherServlet() {
DispatcherServlet servlet = new DispatcherServlet();
servlet.setContextClass(AnnotationConfigWebApplicationContext.class);
servlet.setContextConfigLocation("com.example.config.ApiConfig");
return servlet;
}
@Bean
public ServletRegistrationBean<DispatcherServlet> apiServletRegistration() {
ServletRegistrationBean<DispatcherServlet> registration =
new ServletRegistrationBean<>(apiDispatcherServlet(), "/api/*");
registration.setName("apiDispatcher");
registration.setLoadOnStartup(1);
return registration;
}
@Bean
public DispatcherServlet webDispatcherServlet() {
DispatcherServlet servlet = new DispatcherServlet();
servlet.setContextClass(AnnotationConfigWebApplicationContext.class);
servlet.setContextConfigLocation("com.example.config.WebConfig");
return servlet;
}
@Bean
public ServletRegistrationBean<DispatcherServlet> webServletRegistration() {
ServletRegistrationBean<DispatcherServlet> registration =
new ServletRegistrationBean<>(webDispatcherServlet(), "/web/*");
registration.setName("webDispatcher");
registration.setLoadOnStartup(2);
return registration;
}
}
容器初始化流程图
text
Servlet容器启动
↓
ContextLoaderListener.contextInitialized()
↓
ContextLoader.initWebApplicationContext()
↓ 创建根容器
↓ 配置RootConfig
↓ refresh()
↓ 存入ServletContext
↓
DispatcherServlet.init()
↓
HttpServletBean.init() - 解析init-param
↓
FrameworkServlet.initServletBean()
↓
initWebApplicationContext()
├─ 获取根容器
├─ 创建子容器
├─ 设置父子关系
└─ configureAndRefreshWebApplicationContext()
↓
onRefresh(wac)
↓
initStrategies() - 初始化九大组件
父容器管理Service等业务组件,子容器管理Controller等Web组件,子容器可继承父容器Bean。
要点总结
- ContextLoaderListener创建根WebApplicationContext
- DispatcherServlet创建子WebApplicationContext,设置父容器
- 子容器可访问父容器Bean,实现分层架构
- Spring Boot自动配置DispatcherServlet和内嵌容器
- 支持多个DispatcherServlet配置不同URL映射
jwdev/articles/SPRINGMVC/专家/容器级WEB组件扩展/容器级WEB组件扩展/SpringMVC容器初始化与DispatcherServlet.md
📝 发现内容有误?点击此处直接编辑