Spring MVC学习笔记之Spring MVC的Servlet容器初始化和请求处理的设计和实现(二)

1、前言

  在上一篇《Spring MVC的Servlet容器初始化和请求处理的设计和实现》博客中,我们了解了Servlet及容器Tomcat的相关知识,然后详细的分析了Spring MVC实现Servlet标准体系最基础的两个类HttpServletBean和FrameworkServlet,其中FrameworkServlet类中在处理请求过程中,还涉及到的localeContext、requestAttributes和ServletRequestHandledEvent消息发布的相关内容还没有详细分析,我们这一篇就从这里开始进行分析。

2、LocaleContext

  在上一篇中,我们知道在FrameworkServlet类的processRequest()方法中,LocaleContext的处理逻辑如下:

  首先,获取LocaleContextHolder原来保存的LocaleContext实例;然后,构建当前请求的localeContext 实例;再把localeContext 实例设置到LocaleContextHolder,然后进行请求的逻辑处理,处理完成后,再恢复LocaleContextHolder原来保存的localeContext。代码如下所示:

```java
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
	//省略……
	
	//获取LocaleContextHolder原来保存的localeContext 
	LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
	//获取当前请求的localeContext 
	LocaleContext localeContext = buildLocaleContext(request);
	
	//省略……
	
	//将当前请求的LocaleContext和ServletRequestAttributes设置到LocaleContextHolder和RequestContextHolder
	initContextHolders(request, localeContext, requestAttributes);

	//抽象方法,由子类DispatcherServlet实现,请求的实际处理逻辑
	doService(request, response);
		
	//恢复原来的LocaleContext和ServletRequestAttributes
	resetContextHolders(request, previousLocaleContext, previousAttributes);
	//省略……
}

  在处理LocaleContext实例的过程中,使用了一个核心的类LocaleContextHolder,该类用来保存当前线程的LocaleContext信息,而且可以在任何需要的地方通过LocaleContextHolder类获取。在该类中,有两个重要定静态变量,如下所示:

private static final ThreadLocal<LocaleContext> localeContextHolder =
		new NamedThreadLocal<>("LocaleContext");

private static final ThreadLocal<LocaleContext> inheritableLocaleContextHolder =
		new NamedInheritableThreadLocal<>("LocaleContext");

  其中,inheritableLocaleContextHolder 可以被子线程继承。实际上,当LocaleContextHolder可以被子线程继承时,LocaleContext实例的实际存储位置就是变量inheritableLocaleContextHolder 中,反之,存储到变量localeContextHolder 中,在其他地方使用LocaleContext实例时,首先获取inheritableLocaleContextHolder 中的数据,为空时,再获取localeContextHolder 中的数据。

  在LocaleContext上下文实例中,主要存储了Locale和TimeZone实例,即该实例主要是实现了本地化和时区的上下文。这里不再做深入分析。

// Shared default locale at the framework level
@Nullable
private static Locale defaultLocale;

// Shared default time zone at the framework level
@Nullable
private static TimeZone defaultTimeZone;
3、ServletRequestAttributes

  ServletRequestAttributes主要存储了Web请求的相关参数。在FrameworkServlet类的processRequest()方法中过程中,ServletRequestAttributes的处理方式和LocaleContext基本上是一样的。同时也包括了两个ThreadLocal变量,一个用于存储不可继承的变量,一个用于存储可继承的变量,使用逻辑和LocaleContextHolder是一样的,感兴趣的童鞋可以直接看代码。

private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
		new NamedThreadLocal<>("Request attributes");

private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
		new NamedInheritableThreadLocal<>("Request context");
4、ServletRequestHandledEvent

  在FrameworkServlet类的processRequest()方法中,通过调用publishRequestHandledEvent()方法,实现ServletRequestHandledEvent事件的触发。实际上,在方法内通过调用webApplicationContext.publishEvent()方法,实现了事件的出发。

private void publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response,
	long startTime, @Nullable Throwable failureCause) {

	if (this.publishEvents && this.webApplicationContext != null) {
		// Whether or not we succeeded, publish an event.
		long processingTime = System.currentTimeMillis() - startTime;
		this.webApplicationContext.publishEvent(
				new ServletRequestHandledEvent(this,
						request.getRequestURI(), request.getRemoteAddr(),
						request.getMethod(), getServletConfig().getServletName(),
						WebUtils.getSessionId(request), getUsernameForRequest(request),
						processingTime, failureCause, response.getStatus()));
	}
}

  在实际应用中,实现了ApplicationListener接口的类,根据ApplicationEvent的类型进匹配,如果符合条件,就会调用onApplicationEvent()方法,从而实现监听器的回调。需要注意的是,该实现类,需要注入到Spring IOC容器中才会生效。

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

	void onApplicationEvent(E event);

}
5、FrameworkServlet中onRefresh()方法的调用时机

  在上一篇中,我们提到了在抽象类FrameworkServlet的initWebApplicationContext()方法中,调用了onRefresh()模板方法,该方法由子类进行重写实现,并进行相关组件的初始化,即DispatcherServlet通过重写onRefresh()方法,实现了Spring MVC的初始化工作。

  在此之前,我们先补充一下onRefresh()方法的调用时机,在上一篇博客中我们提到了配置或创建webApplicationContext的三种方法,其中只有第二种情况,才会在initWebApplicationContext()方法中调用onRefresh()方法,而第一种和第三种情况,是通过调用configureAndRefreshWebApplicationContext(wac)方法实现了刷新,分析该方法的代码,发现只有wac.refresh();一句代码,这个代码如果实现了调用onRefresh()方法的呢?重新阅读FrameworkServlet的代码,发现有如下的方法:

private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
	public void onApplicationEvent(ContextRefreshedEvent event) {
		FrameworkServlet.this.onApplicationEvent(event);
	}
}


public void onApplicationEvent(ContextRefreshedEvent event) {
	this.refreshEventReceived = true;
	synchronized (this.onRefreshMonitor) {
		onRefresh(event.getApplicationContext());
	}
}

  在前面分析ServletRequestHandledEvent的时候,我们知道,实现了ApplicationListener接口的类,就可以监听ContextRefreshedEvent 事件,并触发onApplicationEvent()方法的回调。在这里,该方法又调用了在FrameworkServlet类最外层定义的onApplicationEvent(ContextRefreshedEvent event)方法,从而实现了针对容器刷新的监听,当调用wac.refresh();方法时,容器进行了刷新,这个时候就会出发监听事件,最终调用了onRefresh()方法,即前面提到的创建webApplicationContext的另外两种方法,都是通过这种方式实现了onRefresh()方法的调用。

6、DispatcherServlet类-概述

  使用过Spring MVC框架的童鞋们都知道,DispatcherServlet类是Spring MVC框架最核心的类,甚至没有之一,该类是请求的核心分发器。在DispatcherServlet类中,除了负责请求的分发,同时在Spring MVC初始化过程中,还负责了Spring MVC框架中九大组件的初始化工作。Spring MVC的九大组件分别是:

  1. HandlerMapping 根据request找到相应的处理器Handler和Interceptors。
  2. HandlerAdapter 使用Handler处理器,进行请求处理的组件。
  3. HandlerExceptionResolver 专门负责对异常情况进行处理的角色。
  4. ViewResolver 逻辑视图解析器,用来将String类型的视图名和Locale解析为View类型的视图。
  5. RequestToViewNameTranslator 从request获取viewName,是ViewResolver 解析器的一种补充。
  6. LocaleResolver 国际化解析器,用于视图解析等。
  7. ThemeResolver 主题解析器。
  8. MultipartResolver 附件解析器
  9. FlashMapManager FlashMap管理器,主要用在处理redirect中传递的参数。
7、Spring MVC组件的初始化

  通过前面的分析,我们知道DispatcherServlet通过重写onRefresh()方法,实现了Spring MVC的初始化工作。下面我们从这个方法,开始分析Spring MVC组件的初始化的流程。

@Override
protected void onRefresh(ApplicationContext context) {
	initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {
	initMultipartResolver(context);
	initLocaleResolver(context);
	initThemeResolver(context);
	initHandlerMappings(context);
	initHandlerAdapters(context);
	initHandlerExceptionResolvers(context);
	initRequestToViewNameTranslator(context);
	initViewResolvers(context);
	initFlashMapManager(context);
}

  在onRefresh()方法中,通过调用initStrategies()实现了组件的初始化。而initStrategies()方法,通过调用组件对应的initXXX()方法,从而实现组件的初始化过程,这里我们以initHandlerMappings()方法为例,来分析HandlerMapping组件的初始化过程,其他组件的实现方式,基本一样。

private void initHandlerMappings(ApplicationContext context) {
	this.handlerMappings = null;
	//根据detectAllHandlerMappings参数判断是否从容器中,获取全部HandlerMapping类型的实例
	if (this.detectAllHandlerMappings) {
		Map<String, HandlerMapping> matchingBeans =
				BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
		if (!matchingBeans.isEmpty()) {//赋值给handlerMappings 属性,并排序
			this.handlerMappings = new ArrayList<>(matchingBeans.values());
			AnnotationAwareOrderComparator.sort(this.handlerMappings);
		}
	}
	else {
		try {
			//获取beanname=handlerMapping的HandlerMapping实例
			HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
			this.handlerMappings = Collections.singletonList(hm);
		}
		catch (NoSuchBeanDefinitionException ex) {
			// Ignore, we'll add a default HandlerMapping later.
		}
	}
	if (this.handlerMappings == null) {
		//如果容器中没有指定的HandlerMapping,则获取默认的HandlerMapping。
		this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
		if (logger.isTraceEnabled()) {
			logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
					"': using default strategies from DispatcherServlet.properties");
		}
	}
}

  当没有从容器中获取到符合条件的HandlerMapping实例,则通过调用getDefaultStrategies()方法获取默认的HandlerMapping。

注意:因为HandlerMapping组件可能是集合类型,即可以有多个所以调用了getDefaultStrategies()方法,如果只能有一个实例的组件,比如LocaleResolver,则会首先调用getDefaultStrategy()方法,然后在该方法中再调用getDefaultStrategies()方法。

protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
	String key = strategyInterface.getName();
	//defaultStrategies中保存的是默认的组件配置,在DispatcherServlet.properties中定义。
	String value = defaultStrategies.getProperty(key);
	if (value != null) {
		//字符串转成字符串数组
		String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
		List<T> strategies = new ArrayList<>(classNames.length);
		for (String className : classNames) {
			try {
				//创建对应组件的实例
				Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
				Object strategy = createDefaultStrategy(context, clazz);
				strategies.add((T) strategy);
			}
			catch (ClassNotFoundException ex) {
				throw new BeanInitializationException(
						"Could not find DispatcherServlet's default strategy class [" + className +
						"] for interface [" + key + "]", ex);
			}
			catch (LinkageError err) {
				throw new BeanInitializationException(
						"Unresolvable class definition for DispatcherServlet's default strategy class [" +
						className + "] for interface [" + key + "]", err);
			}
		}
		return strategies;
	}
	else {//如果没有,则返回空,在九大组件中,除了MultipartResolver,其他都有默认的实例
		return new LinkedList<>();
	}
}


protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {
	return context.getAutowireCapableBeanFactory().createBean(clazz);
}

  在上面方法中,变量defaultStrategies的值,由静态代码块,通过加载spring-webmvc.jar包下的DispatcherServlet.properties的文件,进行初始化的,代码如下:

static {
	try {
		ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
		defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
	}
	catch (IOException ex) {
		throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
	}
}
8、Spring MVC请求处理

  通过前面FrameworkServlet类的学习,我们知道所有的请求,都重新汇聚到了processRequest()方法中,然后再通过调用子类的doService()方法实现请求的处理逻辑,即DispatcherServlet类的请求处理入口是doService()方法。

doService()方法:

  在doService()方法中,实际的处理逻辑交给doDispatch()方法进行处理,在该方法中只是做了:首先判断是不是include请求,如果是则对request的Attribute做个快照备份,等doDispatch()处理完之后(如果不是异步调用且未完成)进行还原,在做完快照后又对request设置了一些属性。

@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
	//请求日志记录
	logRequest(request);
	
	Map<String, Object> attributesSnapshot = null;
	//如果是include请求,则对request的Attribute做个快照备份
	if (WebUtils.isIncludeRequest(request)) {
		attributesSnapshot = new HashMap<>();
		Enumeration<?> attrNames = request.getAttributeNames();
		while (attrNames.hasMoreElements()) {
			String attrName = (String) attrNames.nextElement();
			if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
				attributesSnapshot.put(attrName, request.getAttribute(attrName));
			}
		}
	}

	// 对request设置了一些属性
	request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
	request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
	request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
	request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
	//flashMap相关,主要用于Redirect转发时参数的传递
	if (this.flashMapManager != null) {
		FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
		if (inputFlashMap != null) {
			request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
		}
		request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
		request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
	}

	try {
		doDispatch(request, response);
	}
	finally {
		if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			// Restore the original attribute snapshot, in case of an include.
			if (attributesSnapshot != null) {
				//还原request的快照备份
				restoreAttributesAfterInclude(request, attributesSnapshot);
			}
		}
	}
}

doDispatch()方法(部分,省略了非逻辑的代码):

  在doDispatch()方法中主要做了以下几件事:

  1. 判断request请求类型,即是否是Multipart类型的请求,如果是并封装request请求
  2. 根据request找到Handler,如果没有对应的handler,则调用noHandlerFound()进行处理,并返回 (核心)
  3. 根据Handler找到对应的HandlerAdapter (核心)
  4. 处理LastModified相关内容
  5. 调用前置拦截器
  6. 用HandlerAdapter处理Handler(请求的真正处理逻辑) (核心)
  7. 使用RequestToViewNameTranslator组件从Request获取mv。
  8. 调用后置拦截器
  9. 调用processDispatchResult方法处理上面处理之后的结果,包含找到View并渲染输出给用户 (核心)
  10. 触发回调函数,并清除相关资源
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		
				//判断是否是Multipart类型,并封装request
				processedRequest = checkMultipart(request);

				// 根据request找到Handler
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {//没有handler的处理逻辑
					noHandlerFound(processedRequest, response);
					return;
				}
				// 根据Handler找到对应的HandlerAdapter
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {//处理LastModified相关内容
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}
				//调用前置拦截器
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}
				//用HandlerAdapter处理Handler
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
				//使用RequestToViewNameTranslator组件从Request获取mv。
				applyDefaultViewName(processedRequest, mv);
				//调用后置拦截器
				mappedHandler.applyPostHandle(processedRequest, response, mv);
				
			//调用processDispatchResult方法处理上面处理之后的结果,包含找到View并渲染输出给用户
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			//触发afterCompletion回调
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			//触发afterCompletion回调
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// 当时附件请求时,用于清楚相关资源
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

  doDispatch()方法中,通过使用Spring MVC中的组件实现了Web的请求处理过程,后续在分析具体的组件的时候,我们再详细分析该方法中如何使用组件实现相关的功能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

姠惢荇者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值