• SpringMvc请求流程源码解析


    SpringMVC请求流程

    SpringMvc请求流程图

    image-20220829232553998

    请求流程粗讲解

    当用户发送请求之后,SpringMvc的DispatcherServlet就会收到请求,首先会进去父类的FrameworkServlet#service()
    然后进入HttpServlet#service()
    方法,作用就是判断是什么请求类型的,例如:GET、POST等。这个地方大致过一遍就行,主要是还是 org.springframework.web.servlet.DispatcherServlet#doService
    这个方法回去调用org.springframework.web.servlet.DispatcherServlet#doDispatch
    这才是请求开始的重点方法、对应上图中的请求—>这里便开始了请求的处理。

    org.springframework.web.servlet.DispatcherServlet#doDispatch这个方法中的内容:

    1. 会进行映射,也就是常说的找到Handler,在此步骤中拿到请求地址 例如: /user/info
      对应方法:org.springframework.web.servlet.DispatcherServlet#getHandler
    2. 选择合适的HandlerAdapter映射适配器
    3. 执行前置拦截器 org.springframework.web.servlet.HandlerInterceptor#preHandle
    4. 调用处理适配器执行Handler
    5. 执行后置处理器 ,对应方法: org.springframework.web.servlet.HandlerInterceptor#postHandle
    6. 解析返回值
    7. 执行最终的处理器,也就是视图返回之后的处理器 org.springframework.web.servlet.HandlerInterceptor#afterCompletion

    方法细讲

    doDispatcher --> 核心

    org.springframework.web.servlet.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) {
    					noHandlerFound(processedRequest, response);
    					return;
    				}
    
    				// Determine handler adapter for the current request.
    				// HandlerAdapter  选择处理器适配器;找到最合适的 HandlerAdapter	// 默认返回的是 RequestMappingHandlerAdapter
    				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
    				// Process last-modified header, if supported by the handler.
    				String method = request.getMethod();
    				boolean isGet = HttpMethod.GET.matches(method);
    				if (isGet || HttpMethod.HEAD.matches(method)) {
    					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
    					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
    						return;
    					}
    				}
    
    				// 前置拦截器
    				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    					// 返回false后就不再进行处理了
    					return;
    				}
    
    				// Actually invoke the handler.
                    // 调用HandlerAdapter 的 handle 方法,对请求进行处理
    				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
    				if (asyncManager.isConcurrentHandlingStarted()) {
    					return;
    				}
    
    				// 如果么没有mv,会给一个默认是 mv
    				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);
    				}
    			}
    		}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86

    找到Handler#getHandler

    org.springframework.web.servlet.DispatcherServlet#getHandler

    该方法就是在 doDispatcher()中进行调用的,也就是对应流程中的 第一步:进行映射找到合适的Handler

    看到这个方法的注释就是去找到最合适的Handler,需要方式就是去遍历所有的Handler找到一个合适的就直接返回,这个方法里面就会处理并且找到请求的地址
    例如:http://127.0.0.1/request/mapping 就会把 /request/mapping给拿出来

    方法调用链doDisptcher()->getHandler()->getHandlerInternal()->initLookupPath 会解析出请求路径

    调用栈如下:

    接下来进入 getHandler(processedRequest);方法,传入的参数其实就是 request: HttpServletRequest

    getHandler(request)

    org.springframework.web.servlet.DispatcherServlet#getHandler

    再次方法中会对请求进行处理,并找到合适的 Handler并返回,方法源码如下:

    	@Nullable
    	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    		if (this.handlerMappings != null) {
    			/**
    			 * 拿到所有的处理器映射器(handlerMappings)---容器初始化阶段拿到所有实现了HandlerMapping接口的Bean
    			 * @see DispatcherServlet#initHandlerMappings
    			 * 测试发现:不同的handlerMapping可以有相同的path,谁先解析就用谁
    			 */
    			for (HandlerMapping mapping : this.handlerMappings) {
    				HandlerExecutionChain handler = mapping.getHandler(request);
    				if (handler != null) {
    					return handler;
    				}
    			}
    		}
    		return null;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    再次方法中会调用mapping.getHandler(request)this.handlerMappings
    就是对请求进行处理的处理起,此处测试接口上写得就是 @RequestMapping对应的处理器就是 RequestMappingHandlerMapping
    ,测试发现:找到一个合适处理器就会直接进行返回,意思就是可能不会遍历完所有的处理器,就算后面又能够适配的,但是如果开始又可以处理的就直接返回了: image-20220830145038870

    mapping.getHandler(request)

    这个方法没什么好讲的,感兴趣的可以自己去debug看看,这里讲重要的东西,多个HandlerMethod同时匹配怎么选择的问题,按照spring
    惯用肯定是会返回最合适的一个,就如同推断构造方法进行分值计算一样,下次有空再跟大家分享,推断构造方法。

    这里Debug发现mapping就是RequestMappingHandlerMapping
    ,并且直接匹配进行返回了,这里回去匹配路径,可能会匹配到多个路径,这里就回去选择对应的处理的HandlerMethod
    ,在org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod
    这个方法里面,会经过方法getHandlerInternal()

    getHandlerInternal()
    	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    		// 拿到请求地址,通过 UrlPathHelper 解析的
    		String lookupPath = initLookupPath(request);
    		this.mappingRegistry.acquireReadLock();
    		try {
    			// 通过lookupPath解析最终的handler----HandlerMethod对象
    			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
    			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    		}
    		finally {
    			this.mappingRegistry.releaseReadLock();
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    这个方法中就会去拿到对应的方法路径,并且调用lookupHandlerMethod(lookupPath, request);会返回唯一的HandlerMethod进行进一步封装

    记住return的代码,这里如果找到的 handlerMethod有值就会去调用createWithResolvedBean(),getBean()
    去获取对应的处理Bean,最后将处理方法以及对应的Handler封装到 HandlerMethod中。如何所示:

    image-20220830160010653

    	public HandlerMethod createWithResolvedBean() {
    		Object handler = this.bean;
    		if (this.bean instanceof String beanName) {
    			Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory");
    			handler = this.beanFactory.getBean(beanName);
    		}
    		return new HandlerMethod(this, handler);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    lookupHandlerMethod
    	protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    		List<Match> matches = new ArrayList<>();
    
    		// 根据uri从mappingRegistry.pathLookup获取 RequestMappingInfo
    		// pathLookup会在初始化阶段解析好
    		List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
    		if (directPathMatches != null) {
    			// 如果根据path能直接匹配的RequestMappingInfo 则用该mapping进行匹配其他条件(method、header等)
    			addMatchingMappings(directPathMatches, matches, request);
    		}
    		if (matches.isEmpty()) {
    			// 如果无path匹配,用所有的 RequestMappingInfo 通过AntPathMatcher匹配
    			addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
    		}
    		if (!matches.isEmpty()) {
    			// 选择第一个为最匹配的
    			Match bestMatch = matches.get(0);
    			/*
    				如果匹配到过个
    				@RequestMapping(value="/mappin?")
    				@RequestMapping(value="/mappin*")
    				@RequestMapping(value="/{xxxx}")
    				@RequestMapping(value="/**")
    			 */
    			if (matches.size() > 1) {
    				// 创建 MatchComparator 匹配器
    				Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
    
    				/** 根据精准度排序  大概是这样的: ? > * > {} > **   具体可以去看:
    				 * @see org.springframework.util.AntPathMatcher.AntPatternComparator#compare(java.lang.String, java.lang.String)*/
    				matches.sort(comparator);
    
    				// 排完序之后拿到优先等级最高的,也就是第一个
    				bestMatch = matches.get(0);
    				if (logger.isTraceEnabled()) {
    					logger.trace(matches.size() + " matching mappings: " + matches);
    				}
    
    				// 是否配置CORS, 并且是否匹配
    				if (CorsUtils.isPreFlightRequest(request)) {
    					for (Match match : matches) {
    						if (match.hasCorsConfig()) {
    							return PREFLIGHT_AMBIGUOUS_MATCH;
    						}
    					}
    				}
    				else {
    					// 获得第二匹配的。如果和第一个一样,则抛出异常
    					Match secondBestMatch = matches.get(1);
    					if (comparator.compare(bestMatch, secondBestMatch) == 0) {
    						Method m1 = bestMatch.getHandlerMethod().getMethod();
    						Method m2 = secondBestMatch.getHandlerMethod().getMethod();
    						String uri = request.getRequestURI();
    						throw new IllegalStateException(
    								"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
    					}
    				}
    			}
    			// 将最匹配的设置到 request 中
    			request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
    			handleMatch(bestMatch.mapping, lookupPath, request);
    			// 返回最匹配的
    			return bestMatch.getHandlerMethod();
    		}
    		else {
    			// return null
    			return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    在源码注释中已经表示了匹配多个的情况是怎么区分的,匹配多个的情况并且想要整长执行必须是使用通配符的方式(? > * > {} > **)
    ,如果出现两个相同的路径,mvc则会抛出异常,讲到这里差不多第一个步骤就结束了,找到了合适的 handlerMethod
    并且将HandlerMethod存储在了request中;

    • 存储:request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
    • 存储的key:String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";
    • key= org.springframework.web.servlet.HandlerMapping.bestMatchingHandler

    找到之后就会返回对应的Handler,就是能够处理这个请求的那个 Bean,也是就是我们程序员些的 Controller

    这里寻找HandlerMethod会直接使用解析出来的路径去pathLookup中去拿。pathLookup
    是一个map,在springmvc启动的时候就会解析我们定义的@RequestMapping中的值,并作为key存储在pathLookup
    中;下一次讲springmvc启动流程的时候在解释

    public List<T> getMappingsByDirectPath(String urlPath){
            return this.pathLookup.get(urlPath);
            }
    
    • 1
    • 2
    • 3

    image-20220830174101017

    找到HandlerAdapter#getHandlerAdapter

    org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter

    这个点对应请求流程图中的第二步,找到合适的HandlerAdapter,我们看一下具体是怎么找的

    getHandlerAdapter

    这里传进来的就是 在第一步中找到的:HandlerMethod,这里和寻找Handler
    一个套路的,找到合适的直接返回,不会再去走下面的 HandlerAdapter

    	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    		if (this.handlerAdapters != null) {
    			for (HandlerAdapter adapter : this.handlerAdapters) {
    				if (adapter.supports(handler)) {
    					return adapter;
    				}
    			}
    		}
    		throw new ServletException("No adapter for handler [" + handler +
    				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    adapter.supports(handler)

    看一下个方法,其实也没什么,只是判断当前的HandlerAdapter是否支持处理 handlerMethod

    	public final boolean supports(Object handler) {
    		return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
    	}
    
    • 1
    • 2
    • 3

    supportsInternal((HandlerMethod) handler) 这个方法在 RequestMappingHandlerAdapter类中默认返回 true

    执行前置拦截器

    org.springframework.web.servlet.HandlerExecutionChain#applyPreHandle

    	boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    		for (int i = 0; i < this.interceptorList.size(); i++) {
    			HandlerInterceptor interceptor = this.interceptorList.get(i);
    			if (!interceptor.preHandle(request, response, this.handler)) {
    				triggerAfterCompletion(request, response, null);
    				return false;
    			}
                // 存储执行到了哪些拦截器,如果出现了前置拦截器返回false的情况,那么最终拦截器也只执行到i下标的那一个
    			this.interceptorIndex = i;
    		}
    		return true;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这个没啥好讲的,大家看一下这个源码,意思就是遍历扫描的时候拿到的所有的拦截器(实现了 HandlerIntercepter接口的)
    ,拿出来全部调用其preHandle 方法

    **注意:**如果前置拦截器返回了false
    那么意思就代表此请求被拦截掉了,要去执行最终拦截器,这个点放到流程最后讲。也就是调用拦截器的afterCompletion方法

    执行Handler–会去执行真正的方法

    org.springframework.web.servlet.HandlerAdapter#handle

    handle()->handleInternal,最红会执行到下面的handleInternal方法

    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal

    方法执行栈:

    image-20220830163838408

    方法执行链:

    • handle: 执行controller方法的的进入点
    • getMethodArgumentValue:解析方法入参,解析完就回去真正的执行HandlerMethod
    protected ModelAndView handleInternal(HttpServletRequest request,
            HttpServletResponse response,HandlerMethod handlerMethod)throws Exception{
    
            ModelAndView mav;
    
            // 检查当前请求的method是否为支持的method(默认为null,可以通过继承AbstractController设置supportedMethod)
            // 检查当前请求是够必须 session(默认为false,可以通过继承AbstractController 设置requireSession)
            checkRequest(request);
    
            /**
             * 判断当前是否需要支持在同一个session中只能线性地处理请求
             * 因为锁是通过 synchronized 是 JVM 进程级,所以在分布式环境下,
             * 无法达到同步相同 Session 的功能。默认情况下,synchronizeOnSession 为 false
             */
            // Execute invokeHandlerMethod in synchronized block if required.
            if(this.synchronizeOnSession){
    
            // 获取当前请求的 Session 对象
            HttpSession session=request.getSession(false);
            if(session!=null){
            // 为当前session 生成唯一的一个可以用于锁定的key
            Object mutex=WebUtils.getSessionMutex(session);
    synchronized (mutex){
            // 对HandlerMethod进行参数等的适配处理,并调用目标handler
            mav=invokeHandlerMethod(request,response,handlerMethod);
            }
            }else{
            // 如果当前不存在session,则直接对HandlerMethod进行适配
            // No HttpSession available -> no mutex necessary
            mav=invokeHandlerMethod(request,response,handlerMethod);
            }
            }
            else{
            // No synchronization on session demanded at all...
            // *如果当前不需要对session进行同步处理,则直接对HandlerMethod进行适配
            mav=invokeHandlerMethod(request,response,handlerMethod);
            }
    
            //判断当前请求头中是否包含Cache-Control请求头,如果不包含,则对当前response进行处理
            if(!response.containsHeader(HEADER_CACHE_CONTROL)){
            if(getSessionAttributesHandler(handlerMethod).hasSessionAttributes()){
            applyCacheSeconds(response,this.cacheSecondsForSessionAttributeHandlers);
            }
            else{
            prepareResponse(response);
            }
            }
    
            return mav;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    在这个方法中我们主要看invokeHandlerMethod()方法,从方法名称就能看出是去执行开始选出来的handlerMethod
    方法,也就是我们自己写的controller的方法,下面看一下该方法的源码,方法有中文注释,推荐自己debug看一遍

    invokHandlerMethod
    @Nullable
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
            HttpServletResponse response,HandlerMethod handlerMethod)throws Exception{
    
            // 将request response 包装成 ServletWebRequest
            ServletWebRequest webRequest=new ServletWebRequest(request,response);
            try{
            // 获取容器中全局配置的InitBinder和当前HandlerMethod所对应的Controller中
            // 配置的InitBinder,用于进行参数的绑定
            WebDataBinderFactory binderFactory=getDataBinderFactory(handlerMethod);
    
            // 获取容器中全局配置的ModelAttribute和当前HandlerMethod所对应的Controller 中配置的ModelAttribute,
            // 这些配置的方法将会在目标方法调用之前进行调用
            ModelFactory modelFactory=getModelFactory(handlerMethod,binderFactory);
    
    
            // 封装HandlerMethod,会在调用前进行参数解析,调用后对返回值进行处理
            ServletInvocableHandlerMethod invocableMethod=createInvocableHandlerMethod(handlerMethod);
            if(this.argumentResolvers!=null){
            // 让invocableMethod 有解析参数的能力
            invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
            }
            if(this.returnValueHandlers!=null){
            // 让 invocableMethod 有处理返回值的能力
            invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
            }
    
            // 让invocableMethod拥有InitBinder解析能力
            invocableMethod.setDataBinderFactory(binderFactory);
            // 设置ParameterNameDiscoverer,该对象将按照一定的规则获取当前参数的名称
            invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
    
            // ModelAndView处理容器
            ModelAndViewContainer mavContainer=new ModelAndViewContainer();
            // 将request的Attribute复制一份到ModelMap
            mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
            // *调用我们标注了@ModelAttribute的方法,主要是为我们的目标方法预加载
            modelFactory.initModel(webRequest,mavContainer,invocableMethod);
            // 重定向的时候,忽略model中的数据 默认false
            mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
    
            // 获取当前的AsyncWebRequest,这里AsyncWebRequest的主要作用是用于判断目标
            // handler的返回值是否为WebAsyncTask或DeferredResult,如果是这两种中的一种,
            // 则说明当前请求的处理应该是异步的。所谓的异步,指的是当前请求会将Controller中
            // 封装的业务逻辑放到一个线程池中进行调用,待该调用有返回结果之后再返回到response中。
            // 这种处理的优点在于用于请求分发的线程能够解放出来,从而处理更多的请求,提高吞吐。
            // 只有待目标任务完成之后才会回来将该异步任务的结果返回。
            AsyncWebRequest asyncWebRequest=WebAsyncUtils.createAsyncWebRequest(request,response);
            asyncWebRequest.setTimeout(this.asyncRequestTimeout);
            // 封装异步任务的线程池、request、interceptors到WebAsyncManager中
            WebAsyncManager asyncManager=WebAsyncUtils.getAsyncManager(request);
            asyncManager.setTaskExecutor(this.taskExecutor);
            asyncManager.setAsyncWebRequest(asyncWebRequest);
            asyncManager.registerCallableInterceptors(this.callableInterceptors);
            asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
    
            // 这里就是用于判断当前请求是否有异步任务结果的,如果存在,则对异步任务结果进行封装
            if(asyncManager.hasConcurrentResult()){
            Object result=asyncManager.getConcurrentResult();
            mavContainer=(ModelAndViewContainer)asyncManager.getConcurrentResultContext()[0];
            asyncManager.clearConcurrentResult();
            LogFormatUtils.traceDebug(logger,traceOn->{
            String formatted=LogFormatUtils.formatValue(result,!traceOn);
            return"Resume with async result ["+formatted+"]";
            });
            invocableMethod=invocableMethod.wrapConcurrentResult(result);
            }
            // *对请求参数进行处理,调用目标HandlerMethod,并且将返回值封装为一个ModelAndView对象     很重要
            invocableMethod.invokeAndHandle(webRequest,mavContainer);
            if(asyncManager.isConcurrentHandlingStarted()){
            return null;
            }
    
            // 对封装的ModelAndView进行处理,主要是判断当前请求是否进行了重定向,如果进行了重定向,
            // 还会判断是否需要将FlashAttributes封装到新的请求中
            return getModelAndView(mavContainer,modelFactory,webRequest);
            }
            finally{
            webRequest.requestCompleted();
            }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    上述方法重要点就在 invocableMethod.invokeAndHandle(webRequest, mavContainer);,这里就回去执行方法并且处理返回的对象

    invokeAndHandle
    public void invokeAndHandle(ServletWebRequest webRequest,ModelAndViewContainer mavContainer,
            Object...providedArgs)throws Exception{
            /* 真正的调用目标方法。很重要、很重要*/
            Object returnValue=invokeForRequest(webRequest,mavContainer,providedArgs);
            // 设置相关的返回状态
            setResponseStatus(webRequest);
    
            // 如果请求完成,则设置requestHandler 属性
            if(returnValue==null){
            if(isRequestNotModified(webRequest)||getResponseStatus()!=null||mavContainer.isRequestHandled()){
            disableContentCachingIfNecessary(webRequest);
            mavContainer.setRequestHandled(true);
            return;
            }
            }
            // 如果请求失败但是有错误原因,也会设置 requestHandler 属性
            else if(StringUtils.hasText(getResponseStatusReason())){
            mavContainer.setRequestHandled(true);
            return;
            }
    
            mavContainer.setRequestHandled(false);
            Assert.state(this.returnValueHandlers!=null,"No return value handlers");
            try{
            // 遍历当前容器中所有ReturnValueHandler,判断哪种handler支持当前返回值的处理,
            // 如果支持,则使用该handler处理该返回值
            this.returnValueHandlers.handleReturnValue(
            returnValue,getReturnValueType(returnValue),mavContainer,webRequest);
            }
            catch(Exception ex){
            if(logger.isTraceEnabled()){
            logger.trace(formatErrorForReturnValue(returnValue),ex);
            }
            throw ex;
            }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    invokeForRequest方法中有一个很重要的方法 getMethodArgumentValues

    getMethodArgumentValues
    protected Object[]getMethodArgumentValues(NativeWebRequest request,@Nullable ModelAndViewContainer mavContainer,
            Object...providedArgs)throws Exception{
            // 获取目标方法参数的描述数组对象
            MethodParameter[]parameters=getMethodParameters();
            if(ObjectUtils.isEmpty(parameters)){
            return EMPTY_ARGS;
            }
            //用来初始化我们对应参数名称的参数值得数组
            Object[]args=new Object[parameters.length];
            // 循环拿到参数名数组
            for(int i=0;i<parameters.length;i++){
            MethodParameter parameter=parameters[i];
            //为我们得MethodParameter设置参数名称探测器对象
            parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
            args[i]=findProvidedArgument(parameter,providedArgs);
            if(args[i]!=null){
            continue;
            }
    
            // * 获取所有的参数解析器,然后筛选出合适的解析器
            if(!this.resolvers.supportsParameter(parameter)){
            throw new IllegalStateException(formatArgumentError(parameter,"No suitable resolver"));
            }
            try{
    
            // 通过上面筛选的 参数解析器来解析我们的参数
            args[i]=this.resolvers.resolveArgument(parameter,mavContainer,request,this.dataBinderFactory);
            }catch(Exception ex){
            // Leave stack trace for later, exception may actually be resolved and handled...
            if(logger.isDebugEnabled()){
            String exMsg=ex.getMessage();
            if(exMsg!=null&&!exMsg.contains(parameter.getExecutable().toGenericString())){
            logger.debug(formatArgumentError(parameter,exMsg));
            }
            }
            throw ex;
            }
            }
            return args;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    解析完方法后就会去调用doInvokle执行Controller的方法。

    **注意:**在invokAndHandle中有处理返回值的方法调用,也就是下面这个

    // 遍历当前容器中所有ReturnValueHandler,判断哪种handler支持当前返回值的处理,
          // 如果支持,则使用该handler处理该返回值
          this.returnValueHandlers.handleReturnValue(
                returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
    
    • 1
    • 2
    • 3
    • 4
    handleReturnValue
    public void handleReturnValue(@Nullable Object returnValue,MethodParameter returnType,
            ModelAndViewContainer mavContainer,NativeWebRequest webRequest)throws Exception{
            // 根据返回的类型选择返回值解析器
            HandlerMethodReturnValueHandler handler=selectHandler(returnValue,returnType);
            if(handler==null){
            throw new IllegalArgumentException("Unknown return value type: "+returnType.getParameterType().getName());
            }
            handler.handleReturnValue(returnValue,returnType,mavContainer,webRequest);
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这个selectHandler选择返回值解析器和之前的是一个套路,找到了直接返回解析器,然后调用解析器的HandleReturnValue
    进行处理,这里是返回的ModelAndVierw也就是jsp,对应的处理器就是ModelAndViewMethodValueHandler,如果是Json
    那么对应的是ReqeustResponseBodyMethodProcessor

    image-20220830165026940

    image-20220830170312989

    这里ModelAndView的返回类型的处理,方法在下面一个类中,自己也能够看懂,讲到这里返回值返回ModelAndView就结束了。下面就是查找视图以及渲染视图

    org.springframework.web.servlet.mvc.method.annotation.ModelAndViewMethodReturnValueHandler#handleReturnValue

    查找视图

    org.springframework.web.servlet.DispatcherServlet#processDispatchResult

    在此方法中进行处理,根据方法名processDispatchResult得知,处理转发结果

    private void processDispatchResult(HttpServletRequest request,HttpServletResponse response,
    @Nullable HandlerExecutionChain mappedHandler,@Nullable ModelAndView mv,
    @Nullable 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.isTraceEnabled()){
            logger.trace("No view rendering, null ModelAndView returned.");
            }
            }
    
            if(WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()){
            // Concurrent handling started during a forward
            return;
            }
    
            if(mappedHandler!=null){
            // Exception (if any) is already handled.. 拦截器:AfterCompletion
            mappedHandler.triggerAfterCompletion(request,response,null);
            }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    此方法中的重要点render()mappedHandler.triggerAfterCompletion(request, response, null);,可以看到如果出现了异常,会进入异常视图

    • rendee:视图进行渲染
    • triggerAfterCompletion: 执行最终拦截器 afterCompletion
      方法的调用,这里是请求成功没有被拦截,所以直接调用所有的拦截器的 afterCompletion方法
    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!=null?this.localeResolver.resolveLocale(request):request.getLocale());
            response.setLocale(locale);
    
            View view;
            String viewName=mv.getViewName();
            if(viewName!=null){
            // 解析视图名称
            // We need to resolve the view name.
            view=resolveViewName(viewName,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()+"'");
            }
            }
    
            ...省略 log 代码...
            try{
            if(mv.getStatus()!=null){
            request.setAttribute(View.RESPONSE_STATUS_ATTRIBUTE,mv.getStatus());
            response.setStatus(mv.getStatus().value());
            }
            view.render(mv.getModelInternal(),request,response);
            }
            catch(Exception ex){
            ...省略 log 代码...
            throw ex;
            }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    可以看到上面的源码中有一个解析视图名称的代码,其实就是前缀+viewName+后缀,但是:
    这里获取选择对应的视图解析器,和上面的选择套路一样,此处还解析了反正值得前缀以及看是redirect还是forward
    ,根据这个前缀的不同创建不同的视图解析器

    image-20220830172309426

    此处使用的是InternalResourceViewResolver,

    image-20220830172103211

    视图渲染

    view.render(mv.getModelInternal(), request, response);

    最终会走到

    org.springframework.web.servlet.view.InternalResourceView#renderMergedOutputModel

    protected void renderMergedOutputModel(
            Map<String, Object> model,HttpServletRequest request,HttpServletResponse response)throws Exception{
    
            // Expose the model object as request attributes.
            // 将model设置到request 的Attribute 中
            exposeModelAsRequestAttributes(model,request);
    
            // Expose helpers as request attributes, if any.
            // 设置国际化资源
            exposeHelpers(request);
    
            // Determine the path for the request dispatcher.
            // 防止死循环,就是请求路径和转发路径一致
            String dispatcherPath=prepareForRendering(request,response);
    
            // Obtain a RequestDispatcher for the target resource (typically a JSP).
            // 通过 request 拿到 RequestDispatcher;request.getRequestDispacther("/test.jsp")
            RequestDispatcher rd=getRequestDispatcher(request,dispatcherPath);
            if(rd==null){
            throw new ServletException("Could not get RequestDispatcher for ["+getUrl()+
            "]: Check that the corresponding file exists within your web application archive!");
            }
    
            // If already included or response already committed, perform include, else forward.
            if(useInclude(request,response)){
            response.setContentType(getContentType());
            if(logger.isDebugEnabled()){
            logger.debug("Including ["+getUrl()+"]");
            }
            rd.include(request,response);
            }
    
            else{
            // Note: The forwarded resource is supposed to determine the content type itself.
            if(logger.isDebugEnabled()){
            logger.debug("Forwarding to ["+getUrl()+"]");
            }
            // RequestDispatcher.forward直接转发
            rd.forward(request,response);
            }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    最后是直接走的Servlet
    的转发,至此响应客户端完成,还得注意方法中进行了一次判断,防止请求死循环的。就是判断请求路径与转发路径是否一直,如果一致的情况下就会造成请求死循环,例如:请求路径:/user/info
    、转发路径:/user/info,这种情况下就会造成死循环。

    最终处理器

    org.springframework.web.servlet.HandlerExecutionChain#triggerAfterCompletion

    最后进入doDispatcher中的 tiggerAfterCompletion
    方法,这个请求是成功的所以所有的拦截器都需要执行最终拦截,不同于前置拦截器preHandle拦截的时候

    image-20220830173001255

    这里会去直接循环调用拦截器的afterCompletion方法

    void triggerAfterCompletion(HttpServletRequest request,HttpServletResponse response,@Nullable Exception ex){
            for(int i=this.interceptorIndex;i>=0;i--){
            HandlerInterceptor interceptor=this.interceptorList.get(i);
            try{
            interceptor.afterCompletion(request,response,this.handler,ex);
            }
            catch(Throwable ex2){
            logger.error("HandlerInterceptor.afterCompletion threw exception",ex2);
            }
            }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    至此请求结束。

    如文中又错误请指出或者联系我:tianxiang.deng@foxmail.com

  • 相关阅读:
    成人职业教育:知乎、B站、网易“短兵相接”
    LiveGBS流媒体平台GB/T28181功能-国标流媒体服务同时兼容内网收流外网收流多网段设备收流
    golang学习笔记——fmt.Printf()、fmt.Print() 和 fmt.Println()区别
    使C#语言编程更加高效的伎俩
    有关MYSQL的22年最新部分面试题汇总
    通达信接口的定义和实现
    RuntimeError: DataLoader worker (pid(s) 46220) exited unexpectedly
    【JavaSe】断言 assert 到底怎么用?
    两个对象比较内部数据的变化,并返回对应key数组
    【蓝桥杯软件赛 零基础备赛20周】第4周——简单模拟1
  • 原文地址:https://blog.csdn.net/student_547464/article/details/126612131