SpringBoot : 请求是怎么处理的? springboot第八期

文章目录

  • 怎么看源码?
  • 这要从`DispatcherServlet`开始说起
  • 重点 HandlerAdapter 决定一个处理器的适配器
  • mv=ha.handle() 重点 执行目标方法
  • 参数解析器 invokeHandlerMethod
  • 返回值处理器 ValueHandler
  • 如何确定每个参数的数值
  • getMethodArgumentValues是如何获取参数的
  • 挨个判断所有参数解析器看看那个解析器支持解析参数
  • Servlet API
  • 省流总结省流:

怎么看源码?

看源码不要太专注于记方法的名字
多注意流程
多总结谁要去那干了什么 传入了什么返回了什么

这要从DispatcherServlet开始说起

public class DispatcherServlet extends FrameworkServlet {
    
    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 ha = getHandlerAdapter(mappedHandler.getHandler());
                ...
  • HandlerMapping中找到能处理请求的Handler(Controller.method())。
  • 为当前Handler 找一个适配器 HandlerAdapter,用的最多的是RequestMappingHandlerAdapter
  • 适配器执行目标方法并确定方法参数的每一个值。

发生一个请求
在这里插入图片描述
这是controller
SpringBoot : 请求是怎么处理的? springboot第八期_第1张图片

来分析一下是怎么解析的

这里是从handerMapping里面那个能处理我们的请求
在这里插入图片描述
进来看一下
SpringBoot : 请求是怎么处理的? springboot第八期_第2张图片
这里面有我们的映射关系
SpringBoot : 请求是怎么处理的? springboot第八期_第3张图片
在这里插入图片描述
里面的在这里插入图片描述
在这里面找那个是我们需要的
SpringBoot : 请求是怎么处理的? springboot第八期_第4张图片
从这里面挨个遍历知道找到能处理请求的 之前我们说过 他会先找到第一个 如果后续还能找到能处理这个请求的就会进行各种比较排序判断然后报错 报错的信息大概意思是你有几个方法都能处理这个请求
然后这个请求的返回值如下
在这里插入图片描述
找到了能处理我们请求的controller
在这里插入图片描述
这个就是我们上面我们写的方法
在这里插入图片描述

重点 HandlerAdapter 决定一个处理器的适配器

在这里插入图片描述
上面我们已经找到了那个方法能处理我们的请求
这个HandlerAdapter 就是去处理
SpringBoot : 请求是怎么处理的? springboot第八期_第5张图片

这些东西的
进来看一下它是怎么确定的
SpringBoot : 请求是怎么处理的? springboot第八期_第6张图片
看一下这里面都有什么
SpringBoot : 请求是怎么处理的? springboot第八期_第7张图片
0 - 支持方法上标注@RequestMapping
1 - 支持函数式编程的
这个方法会返回找到的符合要求的适配器

我们写的controller都默认在在第0个里面 但是如果我们写的是函数式编程的就会在第二个里面
继续往下面看 下面判断是不是GET
SpringBoot : 请求是怎么处理的? springboot第八期_第8张图片

mv=ha.handle() 重点 执行目标方法

在这里插入图片描述
看看里面的详细内容

  @Nullable
    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        return this.handleInternal(request, response, (HandlerMethod)handler);
    }

再次进入
SpringBoot : 请求是怎么处理的? springboot第八期_第9张图片

 protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        this.checkRequest(request);
        ModelAndView mav;
        if (this.synchronizeOnSession) {
            HttpSession session = request.getSession(false);
            if (session != null) {
                Object mutex = WebUtils.getSessionMutex(session);
                synchronized(mutex) {
                    mav = this.invokeHandlerMethod(request, response, handlerMethod);
                }
            } else {
                mav = this.invokeHandlerMethod(request, response, handlerMethod);
            }
        } else {
            mav = this.invokeHandlerMethod(request, response, handlerMethod);
        }

        if (!response.containsHeader("Cache-Control")) {
            if (this.getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
                this.applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
            } else {
                this.prepareResponse(response);
            }
        }

        return mav;
    }

进入到这里

这里执行目标handler的方法
在这里插入图片描述

参数解析器 invokeHandlerMethod

确定将要执行的目标方法的每一个参数的值是什么;

SpringMVC目标方法能写多少种参数类型。取决于参数解析器argumentResolvers
看看里面到底是怎么执行的
SpringBoot : 请求是怎么处理的? springboot第八期_第10张图片
看看参数解析器
在这里插入图片描述
里面有三十个参数解析器
SpringBoot : 请求是怎么处理的? springboot第八期_第11张图片
这里面确定我们将要执行的每一个参数的参数值是什么
看看这是什么
在这里插入图片描述
这不就是和我们的 @RequestParam 的这个单词一模一样吗
实际上我们用的这个就是由它来解析的

在这里插入图片描述
我们再看看这不都是我们的注解吗?

SpringBoot : 请求是怎么处理的? springboot第八期_第12张图片
也就是说SpringMVC目标方法能写多少种参数类型。取决于参数解析器。
这是我们的参数解析器的接口
SpringBoot : 请求是怎么处理的? springboot第八期_第13张图片
● 当前解析器是否支持解析这种参数
● 支持就调用 resolveArgument

返回值处理器 ValueHandler

在看这个返回值处理器
SpringBoot : 请求是怎么处理的? springboot第八期_第14张图片

在这里插入图片描述
我们下面的这个有多少种返回值取决于上面的这个返回值能设置多少种

SpringBoot : 请求是怎么处理的? springboot第八期_第15张图片
我们的返回值是 在这里插入图片描述

我们的返回值能由下面这么多种
有Model,ResponseBody等等
SpringBoot : 请求是怎么处理的? springboot第八期_第16张图片

这里我们把找好的处理器 返回值都给放进去
SpringBoot : 请求是怎么处理的? springboot第八期_第17张图片
进来看看他要来怎么做
SpringBoot : 请求是怎么处理的? springboot第八期_第18张图片

如何确定每个参数的数值

 public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs);
        this.setResponseStatus(webRequest);
        if (returnValue == null) {
            if (this.isRequestNotModified(webRequest) || this.getResponseStatus() != null || mavContainer.isRequestHandled()) {
                this.disableContentCachingIfNecessary(webRequest);
                mavContainer.setRequestHandled(true);
                return;
            }
        } else if (StringUtils.hasText(this.getResponseStatusReason())) {
            mavContainer.setRequestHandled(true);
            return;
        }

        mavContainer.setRequestHandled(false);
        Assert.state(this.returnValueHandlers != null, "No return value handlers");

        try {
            this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);
        } catch (Exception var6) {
            if (this.logger.isTraceEnabled()) {
                this.logger.trace(this.formatErrorForReturnValue(returnValue), var6);
            }

            throw var6;
        }
    }

第一行执行我们的请求
在这里插入图片描述
然后就跳到这了 开始执行我们的方法
SpringBoot : 请求是怎么处理的? springboot第八期_第19张图片
看看我们是怎么执行这个方法的
在这里插入图片描述
SpringBoot : 请求是怎么处理的? springboot第八期_第20张图片
在这里插入图片描述

首先获得所有的参数
SpringBoot : 请求是怎么处理的? springboot第八期_第21张图片
SpringBoot : 请求是怎么处理的? springboot第八期_第22张图片
SpringBoot : 请求是怎么处理的? springboot第八期_第23张图片
在这里插入图片描述
再看看我们的请求 信息都获取到了
通过反射调用
在这里插入图片描述

getMethodArgumentValues是如何获取参数的

看看源码

 public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
    ...

	@Nullable//InvocableHandlerMethod类的,ServletInvocableHandlerMethod类继承InvocableHandlerMethod类
	public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

        获取方法的参数值
		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);

        ...
       
		return doInvoke(args);
	}
 
    //本节重点,获取方法的参数值
	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];
			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
			args[i] = findProvidedArgument(parameter, providedArgs);
			if (args[i] != null) {
				continue;
			}
            //查看resolvers是否有支持
			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) {
				....
			}
		}
		return args;
	}
    
}

在这里插入图片描述
打个断点
可以看到获取了参数列表
SpringBoot : 请求是怎么处理的? springboot第八期_第24张图片
这里判断参数解析器支不支持参数值
在这里插入图片描述
它是怎么判断的?

挨个判断所有参数解析器看看那个解析器支持解析参数

这是我们的参数列表
SpringBoot : 请求是怎么处理的? springboot第八期_第25张图片
解析参数的源码如下

public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
    
	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return getArgumentResolver(parameter) != null;
	}

	@Override
	@Nullable
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

		HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
		if (resolver == null) {
			throw new IllegalArgumentException("Unsupported parameter type [" +
					parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
		}
		return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
	}
    
    
    @Nullable
	private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
		HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
		if (result == null) {
            //挨个判断所有参数解析器那个支持解析这个参数
			for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
				if (resolver.supportsParameter(parameter)) {
					result = resolver;
					this.argumentResolverCache.put(parameter, result);//找到了,resolver就缓存起来,方便稍后resolveArgument()方法使用
					break;
				}
			}
		}
		return result;
	}
}

我们来具体分析几个
SpringBoot : 请求是怎么处理的? springboot第八期_第26张图片
便利所有的参数解析器 看看谁能支持解析这个参数
SpringBoot : 请求是怎么处理的? springboot第八期_第27张图片
遍历是怎么遍历的?
进来看看 先看第一个
下面考研看到它直接判断你有没有 RequestParam这个注解
SpringBoot : 请求是怎么处理的? springboot第八期_第28张图片
失败了就return false 然后继续遍历下一个
SpringBoot : 请求是怎么处理的? springboot第八期_第29张图片
进入下一个
跟上面的一样 先判断你有没有 RequestParam注解还有是不是Map 的那种 如果不是就继续遍历下一个
SpringBoot : 请求是怎么处理的? springboot第八期_第30张图片
进入下一个
看看参数是不是标了 @ParhVariable注解
在看是不是Map类型的
SpringBoot : 请求是怎么处理的? springboot第八期_第31张图片
下面的就不在遍历了 原理跟上面的一样
如果找到的话就会放到缓存里面
解析的是怎么解析就不说了 过于复杂了
大概就是根据正则然后匹配之类的

Servlet API

像 WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId 这些都是Servlet API
Servlet API 没有注解那么 springboot是怎么获取解析器的?
SpringBoot : 请求是怎么处理的? springboot第八期_第32张图片

其实这些解析器也在我们之前看过的那三十个解析器里面
我们针对在这里插入图片描述
看看它的解析器是怎么处理的

@Override
	public boolean supportsParameter(MethodParameter parameter) {
		Class<?> paramType = parameter.getParameterType();
		return (WebRequest.class.isAssignableFrom(paramType) ||
				ServletRequest.class.isAssignableFrom(paramType) ||
				MultipartRequest.class.isAssignableFrom(paramType) ||
				HttpSession.class.isAssignableFrom(paramType) ||
				(pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||
				Principal.class.isAssignableFrom(paramType) ||
				InputStream.class.isAssignableFrom(paramType) ||
				Reader.class.isAssignableFrom(paramType) ||
				HttpMethod.class == paramType ||
				Locale.class == paramType ||
				TimeZone.class == paramType ||
				ZoneId.class == paramType);
	}
	

return的第一行里面这里 他就直接返回了 要用的解析器
在这里插入图片描述

省流总结省流:

HandlerMapping要去找三个东西

  1. 找能处理请求的Handler
  2. 找适配器 HandlerAdapter ,
  3. 找参数解析器-HandlerMethodArgumentResolver

通过这找到的三个东西解析返回

如何去找?
遍历
这么多的注解
SpringBoot : 请求是怎么处理的? springboot第八期_第33张图片
springboot接收到一个请求的时候他会去找这个那个controller能处理这个请求 就遍历它放请求的那个地方 要是找到了多个就会报错

springboot在加载这些注解的时候他会知道这些注解所在的那个大的类里面有什么注解

然后它想要知道这些注解具体是那个的时候他会挨个匹配加载的所有的注解 要是匹配到了就放到缓存里面 下一次找就会快很多

这都找到了就开始调用这些注解代表的方法去解析 解析的方法就是用正则表达式去匹配 然后获取到value

你可能感兴趣的:(这就是springboot,spring,java,后端)