当ContextLoaderListener初始化完springweb容器的时候,接下来便是springmvc初始化自己的容器。
springmvc将springweb容器作为父容器。web.xml中DispatcherServlet具体配置:
<servlet>
<servlet-name>springServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:/spring-mvc*.xml</param-value>
</init-param>
<!--再启动之前 -->
<load-on-startup>1</load-on-startup>
</servlet>
DispatcherServlet类, extends FrameworkServlet.
而FrameworkServlet extends HttpServletBean implementsApplicationContextAware 。
ApplicationContextAware 主要来设置ApplicationContext中的所有bean,当一个类实现了ApplicationContextAware
那么这个类就会直接获取到spring 配置文件中所有的引用bean对象。接口实现方法:
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
HttpServletBean 继承 HttpServlet 。在web容器启动时调用它的init()方法,其主要工作就是设置我们在web.xml中配置的
contextConfigLocation 属性(文件加载地址),然后调用initServletBean(),该方法由FrameworkServlet 覆盖。
具体代码:
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
// Set bean properties from init parameters.
try {
//加载spring-mvc的配置文件
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
//属性访问器存取Bean对象的属性,所有spring创建的Bean对象,都使用该接口存取Bean属性值
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
//获取服务器信息
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
//模板方法,并没有具体实现
initBeanWrapper(bw);
//将初始化值放到DispatcherServlet中
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
throw ex;
}
// 又子类FrameworkServlet 覆盖
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
和 bean的注入 ,然后调用initServletBean()来完成容器的初始化,其核心是initWebApplicationContext()方法。
具体代码:
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
try {
//初始化容器
this.webApplicationContext = initWebApplicationContext();
//提供子类扩展方法 (并没有实现)
initFrameworkServlet();
}
catch (ServletException ex) {
//省略部分代码
}
}
initWebApplicationContext()具体代码:
protected WebApplicationContext initWebApplicationContext() {
//springweb 容器 (父容器)
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
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);
}
}
}
if (wac == null) {
//查找已经绑定的容器
wac = findWebApplicationContext();
}
if (wac == null) {
//没找到,则创建,拿springweb的rootContext 为父容器
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
//刷新容器 (执行一些初始化,在子类DispatcherServlet中实现)
onRefresh(wac);
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
createWebApplicationContext()具体代码:
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
Class<?> contextClass = getContextClass();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Servlet with name '" + getServletName() +
"' will try to create custom WebApplicationContext context of class '" +
contextClass.getName() + "'" + ", using parent context [" + parent + "]");
}
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
//实例化容器
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
//环境属性
wac.setEnvironment(getEnvironment());
//父亲 root
wac.setParent(parent);
//springmvc 配置文件
wac.setConfigLocation(getContextConfigLocation());
//各种相关配置
configureAndRefreshWebApplicationContext(wac);
return wac;
}
通过FrameworkServlet 完成了springmvc 容器的初始化,并在初始化之后调用onRefresh(wac)来进行属性的初始化
通过模板方式在子类DispatcherServlet进行。
DispatcherServlet 类中具体方法:
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
initStrategies()具体方法:
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context); //文件上传解析,用来支持文件上传
initLocaleResolver(context);//本地化解析,支持国际化 默认解析器AcceptHeaderLocaleResolver 通过解析客户端Locale信息
initThemeResolver(context);//主题解析,动态样式支持,列如换肤
initHandlerMappings(context);//处理器映射器,返回HandlerExecutionChain对象(一个Handler、多个HandlerInterceptor)
initHandlerAdapters(context);//处理器适配器,将处理器进行包装,从而支持多种类型的处理器
initHandlerExceptionResolvers(context);//处理器异常解析,将异常映射到统一的页面
initRequestToViewNameTranslator(context);//处理器没有返回逻辑视图名等相关信息时,自动将请求URL映射为逻辑视图名;
initViewResolvers(context);//视图解析,将逻辑视图名解析为具体视图
initFlashMapManager(context);//链接跳转,通常为重定向
}
调用initStrategies()里面的各种属性初始化方法,springmvc的容器初始化工作也便慢慢结束。