zoukankan      html  css  js  c++  java
  • SpringMVC源码情操陶冶-DispatcherServlet简析(二)

    承接前文SpringMVC源码情操陶冶-DispatcherServlet类简析(一),主要讲述初始化的操作,本文将简单介绍springmvc如何处理请求

    DispatcherServlet#doDispatch()

    DispatcherServlet复写父类的doService()方法,其中最主要的处理客户端发的请求便是doDispath()方法,我们只深究此方法,大致上看下其中的逻辑

    注释瞧一发

    /**
    	 * 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
    	 */
    

    由官方注释中可得出以下结论:

    1. 所有的关于http协议的方法都是通过本方法来处理

    2. 处理过程中,handler处理器是核心,优先是从HandlerMappings中获取,再而可通过HandlerAdapter适配器来再一层包装供支持更多形式的请求

    源码简析

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    		//这里的processedRequest主要用于文件上传类的请求
    		HttpServletRequest processedRequest = request;
    		//mappedHandler是处理的核心,此处可以理解为处理链
    		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.优先从HandlerMappings中获取处理器对象
    				mappedHandler = getHandler(processedRequest);
    				//这里找不到handler则会出现我们熟悉的"No mapping found"日志打印
    				if (mappedHandler == null || mappedHandler.getHandler() == null) {
                                    //返回404错误
    					noHandlerFound(processedRequest, response);
    					return;
    				}
    
    				//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)) {
    					//获取上次修改事时间,第一次访问为-1L
    					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
    					if (logger.isDebugEnabled()) {
    						logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
    					}
    					//对未修改的资源get请求,直接返回302状态码
    					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
    						return;
    					}
    				}
    				//拦截器处理请求,调用拦截接口preHandle方法,一旦HandlerInteceptor返回false,则表示拦截成功
    				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    					return;
    				}
    
    				// Actually invoke the handler.通过HandlerAdapter处理请求返回逻辑试图
    				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    				
    				//异步请求直接返回
    				if (asyncManager.isConcurrentHandlingStarted()) {
    					return;
    				}
    				//倘若mv对象内部没有逻辑视图名则采取默认视图
    				applyDefaultViewName(processedRequest, mv);
    				//调用拦截器接口的postHandle()接口
    				mappedHandler.applyPostHandle(processedRequest, response, mv);
    			}
    			catch (Exception ex) {
    				//处理过程中出现了异常
    				dispatchException = ex;
    			}
    			//再次处理,如果有异常出现则需要处理异常信息,因为异常也可有对应的视图
    			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    		}
    		catch (Exception ex) {
                            //processDispatchResult()出现异常时调用
    			//倒序调用之前已调用过的HandlerInteceptor接口的afterCompletion()方法,再直接返回异常信息给客户端
    			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    		}
    		catch (Error err) {
                            //processDispatchResult()出现异常时调用
    		        //倒序调用之前已调用过的HandlerInteceptor接口的afterCompletion()方法,再直接返回异常信息给客户端
    			triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
    		}
    		finally {
    			if (asyncManager.isConcurrentHandlingStarted()) {
    				// Instead of postHandle and afterCompletion
    				if (mappedHandler != null) {
    					//调用所有的实现了AsyncHandlerInterceptor接口的afterConcurrentHandlingStarted()方法
    					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
    				}
    			}
    			else {
    				// Clean up any resources used by a multipart request.
    				if (multipartRequestParsed) {
    					//清除文件上传请求的相关信息,释放资源
    					cleanupMultipart(processedRequest);
    				}
    			}
    		}
    	}
    

    基本操作逻辑为:

    1. 根据请求路径查找是否存在某个HandlerMapping与之对应

    2. 根据HandlerMapping获取HandlerExecutionChain处理链,内部包含一系列的拦截器以及真实处理的Handler对象

    3. 对处理链返回的Handler或者其本身也为空,则直接返回404错误

    4. 根据HandlerExecutionChain处理链中的Handler对象获取HandlerAdapter适配器,如果没有找到则直接返回异常

    5. 对GET/HEAD请求做Last-Modified校验,如果是第二次重复请求则直接返回302状态

    6. 使用HandlerExecutionChain处理链中的interceptors拦截器依次调用preHandler()预拦截方法,如果拦截器执行过程中出现return false的情况,则直接被拦截返回

    7. 通过HandlerAdapter适配器的handle()方法返回视图对象

    8. 使用HandlerExecutionChain处理链中的interceptors拦截器依次调用postHandler()方法,对返回内容进行补充

    9. 对视图对象进行渲染返回

    10. 如果在处理过程中出现了异常,则对异常进行相应的抛出或者异常视图的渲染即可能直接返回粗暴的异常信息或者友好的错误信息页面

    DispatcherServlet#getHandler()-获取HandlerExecutionChain处理链

    简单代码如下

    	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    		for (HandlerMapping hm : this.handlerMappings) {
    			//根据HandlerMapping对象获取处理链
    			HandlerExecutionChain handler = hm.getHandler(request);
    			if (handler != null) {
    				return handler;
    			}
    		}
    		return null;
    	}
    

    主要是注意此处的handlerMappings属性,根据我们在springmvc配置中常用mvc:annotation-driven节点,我们可以得知包含的属性主要有

    • RequestMappingHandlerMapping 主要解析@Controller注解类并解析其中包含@RequestMapping的注解方法包装为HandlerMethod作为handler对象
    • BeanNameUrlHandlerMapping 对含有/开头的beanName注册为handler,handler对象则为其本身在springmvc上下文中对应的class类实例
    • SimpleUrlHandlerMapping 即采用urlMap保存路径与处理类的关系,即可以直接指定url对应handler对象,其中handler对象多为beanName对应的class类【此处不包含】

    具体HanlderMapping获取处理链对象是通过抽象类AbstractHandlerMapping#getHandler()来操作的,限于篇幅过长,遂独立成篇>>>SpringMVC源码情操陶冶-AbstractHandlerMapping

    DispatcherServlet#getHandlerAdapter()-获取Handler适配器

    获取适配器的目的是为了通过其获取视图对象

    	//参数handler一般为具体对象
    	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    		//此处的handler可为HandlerMethod/HttpRequestHandler/Controller/Servlet
    		for (HandlerAdapter ha : this.handlerAdapters) {
                    //supports方法代表其支持何种handler对象,也就是适配器的意义所在
    			if (ha.supports(handler)) {
    				return ha;
    			}
    		}
    		//此处可知当HandlerAdapter没有找到则会抛出ServletException异常
    		throw new ServletException("No adapter for handler [" + handler +
    				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    	}
    

    HandlerExecutionChain#applyPreHandle()-对请求进行拦截的预处理

    调用对应请求路径的拦截器集合的统一方法,源码奉上

    	boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    		//获取内部的HandlerInterceptor集合
    		HandlerInterceptor[] interceptors = getInterceptors();
    		if (!ObjectUtils.isEmpty(interceptors)) {
    			//依次执行
    			for (int i = 0; i < interceptors.length; i++) {
    				HandlerInterceptor interceptor = interceptors[i];
    				//调用拦截器的preHandle()方法,一旦返回false则提前结束请求
    				if (!interceptor.preHandle(request, response, this.handler)) {
    					triggerAfterCompletion(request, response, null);
    					return false;
    				}
    				this.interceptorIndex = i;
    			}
    		}
    		return true;
    	}
    
    • 执行对应请求路径的拦截器集合的统一方法preHandler()方法,拦截器一般都是HandlerInterceptor接口的实现类

    • 一旦顺序执行过程中,出现preHandler()方法中返回false,则表示直接返回,true代表往下执行下一个拦截器

    HandlerAdapter#handle()-对请求进行业务处理并返回视图对象

    限于篇幅过长,具体可见>>>SpringMVC源码情操陶冶-HandlerAdapter适配器简析

    DispatcherServlet#processDispatchResult()-解析视图对象返回结果

    主要处理视图和异常视图,具体源码奉上

    	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
    			HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
    
    		boolean errorView = false;
    		//对存在异常的优先处理
    		if (exception != null) {
    			//是否为ModelAndViewDefinitionException异常
    			if (exception instanceof ModelAndViewDefiningException) {
    				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);
    			//针对异常的视图,清除request对象中的error属性
    			if (errorView) {
    				WebUtils.clearErrorRequestAttributes(request);
    			}
    		}
    
    		if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
    			// Concurrent handling started during a forward
    			return;
    		}
    		
    		//执行拦截器中的afterCompletion()方法
    		if (mappedHandler != null) {
    			mappedHandler.triggerAfterCompletion(request, response, null);
    		}
    	}
    

    由以上代码可知springmvc优先对异常作视图解析,然后通过render()方法渲染视图返回给前台。这其中也会调用拦截器的afterCompletion()方法来收尾

    DispatcherServlet#processHandlerException()-处理异常返回ModelAndView

    通过已注册的HandlerExceptionResolver集合来解析异常返回视图,源码奉上

    	protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
    			Object handler, Exception ex) throws Exception {
    
    		// Check registered HandlerExceptionResolvers...
    		ModelAndView exMv = null;
    		//遍历异常类解析器集合,解析成功则返回
    		for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
    			//解析器解析异常,查找是否有配备的异常视图
    			exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
    			if (exMv != null) {
    				break;
    			}
    		}
    		if (exMv != null) {
    			//判断model和view是否为空
    			if (exMv.isEmpty()) {
    				request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
    				return null;
    			}
    			//尝试设置默认的view对象,默认操作为
    			//比如"/view/req"-->设置"view/req"为viewName
    			if (!exMv.hasView()) {
    				exMv.setViewName(getDefaultViewName(request));
    			}
    			//将异常信息保存至request对象中
    			WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
    			return exMv;
    		}
    		//如果解析器没有解析到合适的视图对象则直接抛出异常
    		throw ex;
    	}
    
    • HandlerExceptionResolver对象处理异常信息的解析,主要查找是否有具体的视图绑定到该异常,具体可查看>>>SpringMVC源码情操陶冶-AbstractHandlerExceptionResolver

    • 当查找到的ModelAndView对象不含有view对象但含有model对象时,其会尝试获取默认视图,比如请求路径为"/view/req"-->设置"view/req"为viewName

    • 当查找不到ModelAndView对象时则直接返回异常,这将导致前端页面会展示具体的异常信息

    DispatcherServlet#render()-渲染视图

    渲染视图返回给前端页面,具体源码奉上

    	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;
    		//判断mv内部属性view是否为string类型
    		if (mv.isReference()) {
    			// 根据mv通过viewResolvers集合(FreemarkerResolver/VelocityResolver..)解析获取视图对象
    			// 包括寻找页面
    			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.
    			//表明存储在ModelAndView对象内部的view要么是String.class类型要么是View.class类型
    			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() + "'");
    			}
    		}
    		try {
    			//最后才是对视图的渲染,返回具体页面给前台
    			view.render(mv.getModelInternal(), request, response);
    		}
    		catch (Exception ex) {
    			//渲染失败仍会抛出异常直接返回给前端
    			throw ex;
    		}
    	}
    
    1. ModelAndView对象中的内部属性view要么是String.class类型要么是View.class接口类型,两者选其一

    2. 针对view类型为String.class的,通过springmvc上下文已注册的ViewResolver集合,调用其统一一接口方法resolveViewName()方法来获取View对象。具体>>>SpringMVC源码情操陶冶-ViewResolver视图解析

    3. 最后渲染视图,并将model等属性绑定到页面引擎中,比如freemarker/groovy等,具体>>>SpringMVC源码情操陶冶-View视图渲染

    4. 当然渲染失败,也会直接将异常显示给前端页面

    小结

    本文只对springmvc如何处理请求并返回作下简单的简析

  • 相关阅读:
    docker-compose
    Cassandra
    npm常用命令
    k8s linux win10
    wsl2 docker 迁移
    docker http 代理
    mysql查看当前所有的数据库和索引大小
    mybatis 遍历list拼接 or查询
    es head crud
    nginx 代理转发mysql
  • 原文地址:https://www.cnblogs.com/question-sky/p/6940552.html
Copyright © 2011-2022 走看看