1.DispatcherServlet作用說明
DispatcherServlet提供Spring Web MVC的集中訪問點,而且負責職責的分派,而且與Spring IoC容器無縫集成,從而可以獲得Spring的所有好處。DispatcherServlet主要用作職責調度工作,本身主要用於控制流程,主要職責如下:
1、文件上傳解析,如果請求類型是multipart將通過MultipartResolver進行文件上傳解析;
2、通過HandlerMapping,將請求映射到處理器(返回一個HandlerExecutionChain,它包括一個處理器、多個HandlerInterceptor攔截器);
3、通過HandlerAdapter支持多種類型的處理器(HandlerExecutionChain中的處理器);
4、通過ViewResolver解析邏輯視圖名到具體視圖實現;
5、本地化解析;
6、渲染具體的視圖等;
7、如果執行過程中遇到異常將交給HandlerExceptionResolver來解析。
2.具體實現說明
看着上面的總結肯定知道了大體上的處理過程,但是對於實際的邏輯實現肯定是一頭霧水,DispatcherServlet的主要處理是在doDispatch()方法中實現的。看看具體的代碼實現:
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);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
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 (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, 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);
}
}
}
}
2.1請求類型處理
首先看代碼:processedRequest = checkMultipart(request);就是為了處理文件類型的請求,如果請求類型是multipart將其轉化通過MultipartResolver進行文件上傳解析;如果不是就返回原來的請求。
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, " +
"this typically results from an additional MultipartFilter in web.xml");
}
else if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) instanceof MultipartException) {
logger.debug("Multipart resolution failed for current request before - " +
"skipping re-resolution for undisturbed error rendering");
}
else {
return this.multipartResolver.resolveMultipart(request);
}
}
// If not returned before: return original request.
return request;
}
首先判斷是不是MultipartHttpServletRequest請求,如果是就不需要再處理了,如果是這繼續判斷是否含有文件解析失敗異常,如果沒有最后才會調用MultipartResolver接口實現類的resolveMultipart()方法來具體處理這個請求並且最后返回MultipartHttpServletRequest。當然了如果不是一個文件類型的請求就會直接的返回原始請求。
2.2 根據請求獲取能夠處理該請求的HandlerExecutionChain
doDispatch()方法中的mappedHandler = getHandler(processedRequest);完成了HandlerExecutionChain(處理器運行鏈,由處理器和處理器攔截器組成)的尋找定位工作。看下如何具體獲取HandlerExecutionChain的過程:
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
這個過程就和SpringMVC處理化的過程有關了,SpringMVC初始化中通過initHandlerMappings(ApplicationContext context)方法初始化了List<HandlerMapping> handlerMappings;也就是通過將所有配置的請求映射放到了這個handlerMappings。這個方法的處理過程簡單就是循環遍歷這個handlerMappings,如果找到了就返回具體的HandlerMapping 中的HandlerExecutionChain,否則就返回null.如果返回的處理器為空,說明沒有能夠處理這個請求的 HandlerMapping,那么就會調用noHandlerFound(processedRequest, response)進行處理,看下noHandlerFound(processedRequest, response)具體的工作。
protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (pageNotFoundLogger.isWarnEnabled()) {
pageNotFoundLogger.warn("No mapping found for HTTP request with URI [" + getRequestUri(request) +
"] in DispatcherServlet with name '" + getServletName() + "'");
}
if (this.throwExceptionIfNoHandlerFound) {
ServletServerHttpRequest sshr = new ServletServerHttpRequest(request);
throw new NoHandlerFoundException(
sshr.getMethod().name(), sshr.getServletRequest().getRequestURI(), sshr.getHeaders());
}
else {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
設置HTTP響應狀態,並且拋出異常。
2.3 獲取請求對應的處理器適配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());決定了獲取請求對應的處理器適配器。看下具體的實現:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
和獲取請求的HandlerExecutionChain對象一樣,遍歷初始化就完成的handlerAdapters,有返回沒有則拋出異常。
2.4 GET 和 HEAD請求處理
對於請求方式是GET或者HEAD的,會判斷是否發生改變,如果沒有改變就直接返回,否則就繼續下去
2.5 調用具體的HandlerAdapter處理請求
方法ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;是使用給定的處理器來處理這個請求,並且返回一個ModelAndView 。以SimpleServletHandlerAdapter為例,其實是調用一個Servlet的Service方法來進行相應的業務邏輯的處理的。
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
((Servlet) handler).service(request, response);
return null;
}
2.6返回視圖的再處理
對上面處理后的視圖或者異常轉化成一個ModelAndView對象:
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
如果有異常則將異常轉化成ModelAndView,如果視圖不為空,則調用render(mv, request, response);進行視圖的解析渲染。
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale = this.localeResolver.resolveLocale(request);
response.setLocale(locale);
View view;
if (mv.isReference()) {
// We need to resolve the view name.
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isDebugEnabled()) {
logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
}
try {
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
getServletName() + "'", ex);
}
throw ex;
}
}
首先獲取請求的本地化處理並且將其設置到response中,如果指定了視圖名字那么DispatcherServlet就會調用相應的視圖解析器來進行解析,返回View對象
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
HttpServletRequest request) throws Exception {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
如果ModelAndView 沒有視圖引用說明,它已經含有了一個真實的View對象直接過去即可。如果獲取的View對象為空則需要爆出異常。最后也是最重要的就是調用View對象的render方法( view.render(mv.getModelInternal(), request, response))進行視圖的渲染了。看具體實現是在View的實現抽象類中AbstractView中。
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isTraceEnabled()) {
logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +
" and static attributes " + this.staticAttributes);
}
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
prepareResponse(request, response);
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
簡單的說就是獲取數據並且填充,打完收工。
3.總結
上面便是按照DispatcherServlet從接受到一個請求到請求完全處理完成所經歷的一個過程,很是清晰明確。設計也很簡單。從設計到實現,可以察覺設計模式的幾大原則和具體的設計都有涉及,從中也學到了很多東西,接着學習,加油。
