天天看点

Spring MVC------DispatcherServlet初始化引言DispatcherServlet类关系Spring MVC初始化时序图

引言

Spring MVC的核心是DispatcherServlet

DispatcherServlet类关系

Spring MVC------DispatcherServlet初始化引言DispatcherServlet类关系Spring MVC初始化时序图

1、HttpServletBean继承HttpServlet,因此在Web容器启动时将调用它的init方法,该初始化方法的主要作用将Servlet初始化参数(init-param)设置到该组件上(如contextAttribute、contextClass、namespace、contextConfigLocation),通过BeanWrapper简化设值过程,方便后续使用;提供给子类初始化扩展点,initServletBean(),该方法由FrameworkServlet覆盖。

容器初始化时Servlet的init方法调用GenericServlet的init,如果是tomcat启动会调用DefaultServlet的init()方法进行初始化容器。

第一次调取接口的时候>>>调用GenericServlet的init()初始化子类HttpServletBean的init()方法,最后会调取initServletBean();

Spring MVC------DispatcherServlet初始化引言DispatcherServlet类关系Spring MVC初始化时序图

2、FrameworkServlet继承HttpServletBean,通过initServletBean()进行Web上下文初始化,该方法主要覆盖一下两件事情:

    初始化web上下文;

    提供给子类初始化扩展点;

 最终会由子类FrameworkServlet的initServletBean()实现,源码如下:

/**
	 * Overridden method of {@link HttpServletBean}, invoked after any bean properties
	 * have been set. Creates this servlet's WebApplicationContext.
	 */
	@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 {
            //初始化web上下文
			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");
		}
	}
           
这也是我们经常在控制台看到的日志
           
Spring MVC------DispatcherServlet初始化引言DispatcherServlet类关系Spring MVC初始化时序图

初始化web上下文

/**
	 * Initialize and publish the WebApplicationContext for this servlet.
	 * <p>Delegates to {@link #createWebApplicationContext} for actual creation
	 * of the context. Can be overridden in subclasses.
	 * @return the WebApplicationContext instance
	 * @see #FrameworkServlet(WebApplicationContext)
	 * @see #setContextClass
	 * @see #setContextConfigLocation
	 */
	protected WebApplicationContext initWebApplicationContext() {
        //ROOT上下文(ContextLoaderListener加载的)
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;

		if (this.webApplicationContext != null) {
			// A context instance was injected at construction time -> use it
            // 1、在创建该Servlet注入的上下文
			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) {
            //2、查找已经绑定的上下文
			// 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) {
            //3、如果没有找到相应的上下文,并指定父亲为ContextLoaderListener
			// 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) {
            //4、刷新上下文(执行一些初始化)
				onRefresh(wac);
			}
		}

		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
		}

		return wac;
	}
           

从initWebApplicationContext()方法可以看出,基本上如果ContextLoaderListener加载了上下文将作为根上下文(DispatcherServlet的父容器)。

3.DispatcherServlet继承FrameworkServlet,并实现了onRefresh()方法提供一些前端控制器相关的配置。

最后调用了onRefresh()方法执行容器的一些初始化,这个方法由子类实现,来进行扩展。

Spring MVC------DispatcherServlet初始化引言DispatcherServlet类关系Spring MVC初始化时序图

最终由子类DispatcherServlet实现这个方法onRefresh()

/**
	 * This implementation calls {@link #initStrategies}.
	 */
	@Override
	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}
           
/**
	 * Initialize the strategy objects that this servlet uses.
	 * <p>May be overridden in subclasses in order to initialize further strategy objects.
	 */
	protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
	}
           

initMultipartResolver(context);

初始化MultipartResolver,用于处理文件上传服务,如果有文件上传,那么会将当前的HttpServletRequest包装成DefaultMultipartServletRequest,并且将每个上传的内容封装程CommonsMultipartFile对象。

initLocaleResolver(context);

用于处理应用的国际化问题,通过解析请求的Locale和设置响应的Locale来控制应用中的字符编码问题。

initThemeResolver(context);

用于定义一个主题,例如,可以根据用户的喜好来设置用户访问的页面的样式,可以将这个样式作为一个Theme Name保存,保存在用于请求的Cookie中或者保存在服务端的session中,以后每次请求根据这个ThemeName返回特定的内容。

initHandlerMappings(context);

用于定义用户设置的请求映射关系,例如,前面示例中的SimpleUrlHandlerMapping把用于用户请求的URL映射成一个个Handler实例。对HandlerMapping必须定义,如果没有定义,将获取DispatcherServlet.properties文件中默认的两个HandlerMapping,分别是BeanNameUrlHandlerMapping和DefaultAnnotationHandlerMapping。

initHandlerAdapters(context);

 用于根据Handler的类型定义不同的处理规则,例如,定义SimpleControllerHandlerAdapter处理所有Controller的实例对象,在HandlerMapping中将URL映射成一个Controller实例,那么Spring MVC在解析时SimpleControllerHandlerAdapter就会调用这个Controller实例。同样对HandlerAdapters也必须定义,将获取DispatcherServlet.properties文件中默认的4个HandlerAdapters,分别是HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter和AnnotationMethodHandlerAdapter。(暂时将HandlerAdapters的作用理解为调用的具体方法)

initHandlerExceptionResolvers(context);

当Handler处理出错时,会通过这个Handler来统一处理,默认的实现类时SimpleMappingExceptionResolver,将错误日志记录在log文件中,并且转到默认的错误页面。

initRequestToViewNameTranslator(context);

将指定的ViewName按照定义的RequestToViewNameTranslator替换成想要的格式,如加上前缀或者后缀

initViewResolvers(context);

用于将View解析成页面,在ViewResolvvers中可以设置多个解析策略,如可以根据JSP来解析,或者按照Velocity模板解析。默认的解析策略是InternalResourceViewResolver,按照JSP页面来解析。

initFlashMapManager(context);

用于准备

DispatcherServlet

处理请求时所使用的

FlashMapManager

策略组件对象。

DispatcherServlet启动时做的事情

httpservlet初始化调用了HttpServletBean的init方法,作用时获取Servlet中的init参数,并创建一个BeanWrapper对象,然后由子类真正执行BeanWrapper的初始化工作。但是HttpServletBean的子类FrameworkServlet和DispatcherServlet都没有覆盖其initBeanWrapper(bw)方法,所以创建的Bean Wrapper对象没有任何作用,Spring容器也不是通过BeanWrapper来创建的。

Spring容器的创建是再FrameworkServlet的initServletBean()方法中完成的,这个方法会创建WebApplicationContext对象,并调用其refresh()方法来完成配置文件的加载,配置文件的加载同样是先查找Servlet的init-param参数中设置的路径,如果没有,会根据namespace+servlet的名称来查找XML文件。Spring容器在加载时会调用DispatcherServlet的initStrategies方法来完成再DisparcherServlet中定义的初始化工作。在initStrategies方法中会初始化Spring MVC框架需要的8个组件,这8个组件对应的8个Bean对象都保存在DispatcherServlet类中。

从如上代码我们可以看出,整个DispatcherServlet初始化的过程和做了些什么事情,具体主要做了如下两件事情:

1、初始化Spring Web MVC使用的Web上下文,并且可能指定父容器为(ContextLoaderListener加载了根上下文);

2、初始化DispatcherServlet使用的策略,如HandlerMapping、HandlerAdapter等。

DispatcherServlet默认配置

DispatcherServlet的默认配置在DispatcherServlet.properties(和DispatcherServlet类在一个包下)中,而且是当Spring配置文件中没有指定配置时使用的默认策略:

Spring MVC------DispatcherServlet初始化引言DispatcherServlet类关系Spring MVC初始化时序图

Spring MVC初始化时序图

Spring MVC------DispatcherServlet初始化引言DispatcherServlet类关系Spring MVC初始化时序图

继续阅读