统一的入口
使用SpringMVC的过程中有种明显感觉就是一个请求不止一次的经过DispatcherServlet,所以有这样一个疑问:DispatcherServlet这种中心式的控制器有什么好处和什么劣势。
看了一些博客之后,总结出一下几点:
- 为Web应用程序提供了一个中心入口点。该集中入口点将系统组件的共同特征进行重新组合。我们可以在那里找到安全资源,语言切换,会话管理,缓存或输入过滤的处理程序。这样做的一个很大的好处是:这个共同的入口点有助于避免代码重复。
- 有助于对以下询问做出最佳响应:
- 如何集中授权和认证?
- 如何处理正确的视图渲染?
- 如何使用URL重写映射将请求发送到适当的控制器?
个人感受较深的是可以对一个请求做统一的附加处理,比如上文说的集中授权和认证。
SpringMVC一条请求的执行过程
DispathcerServlet
上图言简意赅的总结了一条请求过程,对于这个类的分析就可以从上图开始。首先DispatcherServlet名字中带一个Servlet,看一下它的继承关系。
它确实继承了Servlet接口。
还记得Servlet最基本的知识就是Servlet的三大方法:init(),service(),destory(),
init()和destory()在Servlet生命周期里是初始化和销毁的作用,service()才是真正干活的作用,那就来看一下DispatcherServlet真正处理请求的部分。
doService()
方法注释说的是公开DispatcherServlet的特定请求参数,之后将委托为doDispatch作实际的请求分发。 注释说的很清楚,那就不需要过多关注“公开特定请求参数的内容”,直接看doDispatch
/**
* Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
* for the actual dispatching.
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
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));
}
}
}
// Make framework objects available to handlers and view objects.
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());
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) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
doDispatch
当时对方法注释进行阅读: 处理实际分发过程,之后将请求给处理器。 处理器将通过按顺序,应用servlet的处理器映射器获得。 处理器映射器将通过查询servlet已配置的处理器适配器找到第一个支持handler类的。 所有的HTTP方法都会由此方法处理。
/**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 1.调用handlerMapping获取handlerChain
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 2.获取支持该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)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 3.使用HandlerAdapter完成handler处理
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 4.视图处理(页面渲染)
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
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 {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
过程概括:
调用HandleMapping得到handler 调用HandleAdapter执行handle过程(参数解析 过程调用) 调用ViewResolver进行视图解析 渲染视图
来源:oschina
链接:https://my.oschina.net/u/4536753/blog/4478872