1.1 容器初始化
FrameworkServlet.initServletBean 简略版源码:
@Override
protected final void initServletBean() {
this.webApplicationContext = initWebApplicationContext();
//空实现,且没有子类覆盖
initFrameworkServlet()
}
FrameworkServlet.initWebApplicationContext:
protected WebApplicationContext initWebApplicationContext() {
//根容器查找
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
//有可能 DispatcherServlet 被作为 Spring bean 初始化,且 webApplicationContext 已被注入进来
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
//是否已经存在于 ServletContext 中
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;
}
下面分部分展开。
根容器查找
spring-mvc 支持 Spring 容器与 MVC 容器共存,此时,Spring 容器即根容器,mvc 容器将根容器视为父容器。
Spring 容器(根容器) 以下列形式进行配置(web.xml):
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
根据 Servlet 规范,各组件的加载 顺序如下:
listener -> filter -> servlet
WebApplicationContextUtils.getWebApplicationContext:
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}
两参数方法:
public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) {
Object attr = sc.getAttribute(attrName);
if (attr == null) {
return null;
}
return (WebApplicationContext) attr;
}
可以得出结论:
如果 Spring 根容器存在,那么它被保存在 ServletContext 中,其 key 为 WebApplicationContext.class.getName() + ".ROOT" 。
容器创建
FrameworkServlet.createWebApplicationContext:
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
Class<?> contextClass = getContextClass();
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException();
}
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
wac.setParent(parent);
wac.setConfigLocation(getContextConfigLocation());
configureAndRefreshWebApplicationContext(wac);
return wac;
}
通过对 getContextClass 方法的调用,Spring 允许我们自定义容器的类型,即我们可以在 web.xml 中如下配置:
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置文件位置 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-servlet.xml</param-value>
</init-param>
<!-- 容器类型 -->
<init-param>
<param-name>contextClass</param-name>
<param-value>java.lang.Object</param-value>
</init-param>
</servlet>
configureAndRefreshWebApplicationContext 核心源码:
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
applyInitializers(wac);
wac.refresh();
}
ApplicationContextInitializer
ApplicationContextInitializer 允许我们在 Spring(mvc) 容器初始化之前干点坏事,可以通过 init-param 传入:
<init-param>
<param-name>contextInitializerClasses</param-name>
<param-value>坏事儿</param-value>
</init-param>
applyInitializers 方法正是要触发这些坏事儿。类图:

配置解析
"配置"指的便是 spring-servlet.xml:
<context:component-scan base-package="controller"/>
<mvc:annotation-driven/>
<!-- 启用对静态资源使用默认 servlet 处理,非 REST 方式不需要 -->
<mvc:default-servlet-handler/>
<!-- 配置视图 -->
<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<!-- viewClass 属性必不可少 -->
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
<property name="prefix" value="/WEB-INF/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
而解析的入口便在于对 refresh 方法的调用,此方法位于 AbstractApplicationContext,这一点在 spring-core 时已经见过了,下面我们重点关注不同于 spring-core 的地方。
对于 spring-mvc 来说,其容器默认为 XmlWebApplicationContext,部分类图:

XmlWebApplicationContext 通过重写 loadBeanDefinitions 方法改变了 bean 加载行为,使其指向 spring-servlet.xml。
spring-servlet.xml 中不同于 spring-core 的地方便在于引入了 mvc 命名空间,正如 spring-core 中笔记中所说的那样, Spring 用过 jar 包/META-INFO 中的.handlers 文件定义针对不同的命名空间所使用的解析器 。
mvc 命名空间的解析器为 MvcNamespaceHandler,部分源码:
@Override
public void init() {
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
registerBeanDefinitionParser("default-servlet-handler",
new DefaultServletHandlerBeanDefinitionParser());
registerBeanDefinitionParser("interceptors", new IanterceptorsBeanDefinitionParser());
registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser());
}
老样子,按部分展开。
注解驱动
其 parse 方法负责向 Sprng 容器注册一些必要的组件,整理如下图:

静态资源处理
即:
<mvc:default-servlet-handler/>
DefaultServletHandlerBeanDefinitionParser.parse 负责向容器注册以下三个组件:
- DefaultServletHttpRequestHandler
- SimpleUrlHandlerMapping
- HttpRequestHandlerAdapter
拦截器
InterceptorsBeanDefinitionParser.parse 方法负责 将每一项 mvc:interceptor 配置解析为一个 MappedInterceptor bean 并注册到容器中 。
视图
有两种方式向 Spring 容器注册视图:
- 以前采用较土的方式:
<bean class="org.springframework.web.servlet.view.UrlBasedViewResolver"> <!-- viewClass 属性必不可少 --> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property> <property name="prefix" value="/WEB-INF/"></property> <property name="suffix" value=".jsp"></property> </bean> - 通过特定的标签:
<mvc:view-resolvers> <mvc:jsp view-class="" /> </mvc:view-resolvers>
从这里可以推测出: 拦截器同样支持第一种方式,Spring 在查找时应该会查询某一接口的子类。
ViewResolversBeanDefinitionParser.parse 方法的作用便是将每一个视图解析为 ViewResolver 并注册到容器。
Scope/处理器注册
AbstractRefreshableWebApplicationContext.postProcessBeanFactory:
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.addBeanPostProcessor(
new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
beanFactory.ignoreDependencyInterface(ServletConfigAware.class);
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory,
this.servletContext, this.servletConfig);
}
ServletContextAwareProcessor 用以向实现了 ServletContextAware 的 bean 注册 ServletContext。
registerWebApplicationScopes 用以注册"request", "session", "globalSession", "application"四种 scope,scope 是个什么东西以及如何自定义,在 spring-core 中已经进行过说明了。
registerEnvironmentBeans 用以将 servletContext、servletConfig 以及各种启动参数注册到 Spring 容器中。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论