上篇我们详解了 ContextLoaderListener启动过程,这次我们详解DispatcherServlet,先看web.xml 中的相关配置
springmvc org.springframework.web.servlet.DispatcherServlet contextConfigLocation classpath:config/springMVC/spring-*.xml 1 springmvc /
先解释参数含义:DispatcherServlet 核心servlet 类;contextConfigLocation 是springmvc容器加载配置默认的地方,对应的param-value 值是配置文件的地址;load-on-startup,该标签的作用是当 Tomcat 服务器发布 Web 项目时创建 DispatcherServlet 对象并执行起父类的 init 方法,load-on-startup>x中x的取值1,2,3,4,5代表的是优先级,1时优先级最高。url-pattern:过滤所有地址。
1、web服务启动时,同时初始化 DispatcherServlet 父类的init ,因为容器启动首先是启动servlet,DispatcherServlet继承和实现关系如下:
根据以上关系找到init方法,
package javax.servlet;
import java.io.IOException;
public interface Servlet {
// Ctrl+t 找到实现这个方法的实现类
void init(ServletConfig var1) throws ServletException;
ServletConfig getServletConfig();
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
String getServletInfo();
void destroy();
}
2、来到GenericServlet 类中 init方法
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
public void init(ServletConfig config) throws ServletException { this.config = config; this.init();//模板设计模式,子类继续实现,ctrl+t 进入 }
3、来到HttpServletBean 中的 init 方法,注意:HttpServlet继承GenericServlet 类
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
@Override public final void init() throws ServletException { // Set bean properties from init parameters. PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try { BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException ex) { if (logger.isErrorEnabled()) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); } throw ex; } } // Let subclasses do whatever initialization they like. //核心方法,点击进入子类实现的方法 initServletBean(); }
4、来到FrameworkServlet 的initServletBean方法
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
@Override protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'"); if (logger.isInfoEnabled()) { logger.info("Initializing Servlet '" + getServletName() + "'"); } long startTime = System.currentTimeMillis(); try { //核心方法初始化容器,重点看 this.webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); } catch (ServletException | RuntimeException ex) { logger.error("Context initialization failed", ex); throw ex; } if (logger.isDebugEnabled()) { String value = this.enableLoggingRequestDetails ? "shown which may lead to unsafe logging of potentially sensitive data" : "masked to prevent unsafe logging of potentially sensitive data"; logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails + "': request parameters and headers will be " + value); } if (logger.isInfoEnabled()) { logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms"); } } 点击来到
protected WebApplicationContext initWebApplicationContext() { //这里会从servletContext中获取到父容器,就是通过监听器加载的容器 WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } //容器加载配置文件和初始化,重点看,点击进入 configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id wac = findWebApplicationContext(); } if (wac == null) { // No context instance is defined for this servlet -> create a local one wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. synchronized (this.onRefreshMonitor) { onRefresh(wac);//重要初始化,ctrl+t 点击进入子类 } } if (this.publishContext) { // Publish the context as a servlet context attribute. String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); } return wac; }
点击进入:
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { // The application context id is still set to its original default value // -> assign a more useful id based on available information if (this.contextId != null) { wac.setId(this.contextId); } else { // Generate default id... wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName()); } } wac.setServletContext(getServletContext()); wac.setServletConfig(getServletConfig()); wac.setNamespace(getNamespace()); wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); // The wac environment's #initPropertySources will be called in any case when the context // is refreshed; do it eagerly here to ensure servlet property sources are in place for // use in any post-processing or initialization that occurs below prior to #refresh ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig()); } postProcessWebApplicationContext(wac); applyInitializers(wac); wac.refresh();//初始化入口,ctrl+t 进入,后就一目了然。 }
5、onRefresh(wac);ctrl+t 点击进入核心子类DispatcherServlet ,
public class DispatcherServlet extends FrameworkServlet {
@Override protected void onRefresh(ApplicationContext context) { initStrategies(context);//点击进入 }
来到initStrategies方法
protected void initStrategies(ApplicationContext context) { initMultipartResolver(context);//初始化文件上传处理类 initLocaleResolver(context);//初始化本地化Resolver initThemeResolver(context);//初始化主题Resolver initHandlerMappings(context);//初始化一些个与处理的HandlerMappings initHandlerAdapters(context); initHandlerExceptionResolvers(context);//初始化异常处理的handler initRequestToViewNameTranslator(context);//初始化请求路径转换为ViewName 的Translator //初始化ViewResolvers 这个就是针对视图处理的Resolvers 比如jsp处理Resolvers 或者freemarker处理Resolvers initViewResolvers(context); initFlashMapManager(context); }
6,这是DispatcherServlet 启动的整个流程,下篇我们详解 http请求 到 DispatcherServlet 的过程,会更精彩!