6

SpringMvc请求流程源码解析 - BaldHead

 2 years ago
source link: https://www.cnblogs.com/strict/p/16640734.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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);
				}
			}
		}

找到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 会解析出请求路径

调用栈如下:

1986356-20220830203324508-329825790.png

1986356-20220830203324966-6025739.png

接下来进入 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;
	}

再次方法中会调用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();
		}
	}

这个方法中就会去拿到对应的方法路径,并且调用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);
	}

lookupHandlerMethod#

	protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		List<Match> matches = new ArrayList<>();

		// 根据uri从mappingRegistry.pathLookup获取 RequestMappingInfo
		// pathLookup<path,RequestMappingInfo>会在初始化阶段解析好
		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);
		}
	}

在源码注释中已经表示了匹配多个的情况是怎么区分的,匹配多个的情况并且想要整长执行必须是使用通配符的方式(? > * > {} > **)
,如果出现两个相同的路径,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);
        }

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");
	}

adapter.supports(handler)#

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

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

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;
	}

这个没啥好讲的,大家看一下这个源码,意思就是遍历扫描的时候拿到的所有的拦截器(实现了 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;
        }

在这个方法中我们主要看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();
        }
        }

上述方法重要点就在 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;
        }
        }

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;
        }

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

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

// 遍历当前容器中所有ReturnValueHandler,判断哪种handler支持当前返回值的处理,
      // 如果支持,则使用该handler处理该返回值
      this.returnValueHandlers.handleReturnValue(
            returnValue, getReturnValueType(returnValue), mavContainer, webRequest);

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);
        }

这个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);
        }
        }

此方法中的重要点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;
        }
        }

可以看到上面的源码中有一个解析视图名称的代码,其实就是前缀+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);
        }
        }

最后是直接走的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);
        }
        }
        }

至此请求结束。

如文中又错误请指出或者联系我:[email protected]


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK