Spring MVC(一)Servlet 2.x 规范在 Spring MVC 中的应用
Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html)
《Servlet 2.x 规范》:https://www.cnblogs.com/binarylei/p/10205884.html
Servlet 容器在启动时会调用 ServletContextListener 的 contextInitialized() 方法。同时 Servlet 在初始化时会执行 init 方法。Spring MVC 正是在这两个过程中创建 Root WebApplicationContext 和 Servlet WebApplicationContext 容器。
一、ContextLoaderListener
ContextLoaderListener 的 contextInitialized 只是简单的调用了一个其父类 ContextLoader 的 initWebApplicationContext 方法,创建了一个 Root 容器。
// 创建 Root WebApplicationContext
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present");
}
try {
// 1. 创建 Root WebApplicationContext
if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
// 2. 配置 Root WebApplicationContext
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
// 3. 保存 context
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
} else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
}
return this.context;
} catch (RuntimeException | Error ex) {
}
}
二、DispatcherServlet
类继承关系:DispatcherServlet -> FrameworkServlet -> HttpServletBean -> HttpServlet -> Servlet
初始化 DispatcherServlet 时会调用 Servlet 的 init() 方法。
// HttpServletBean#init
@Override
public final void init() throws ServletException {
// 省略...
initServletBean();
}
// FrameworkServlet#initServletBean
@Override
protected final void initServletBean() throws ServletException {
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
至此终于看到 Servlet WebApplicationContext 的创建了。
protected WebApplicationContext initWebApplicationContext() {
// 1. 获取父容器
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
// 2. 子容器已经存在则设置其父容器并启动
if (this.webApplicationContext != null) {
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
// 3. 不存在则创建一个新子容器
if (wac == null) {
wac = findWebApplicationContext();
}
if (wac == null) {
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
onRefresh(wac);
}
if (this.publishContext) {
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
每天用心记录一点点。内容也许不重要,但习惯很重要!