秒懂SpringBoot之Mvc请求执行流程浅谈

Posted ShuSheng007

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了秒懂SpringBoot之Mvc请求执行流程浅谈相关的知识,希望对你有一定的参考价值。

[版权申明] 非商业目的注明出处可自由转载
出自:shusheng007

文章目录

概述

在Java Web开发领域Spring Mvc的流行度居高不下,假期花了天时间又读了下spring mvc的源码,大概整理一下,以便于查询,也便于后辈学习。

SpringMvc 流程简述

先上一张手撕大图…其实只要看懂了这张图对spring mvc的理解也就有了宏观的概念了,看不懂也没关系拉,不耽误我们搬砖挣钱…只不过只能一直搬砖

从图中看,spring mvc 有3大组件:HandlerMapping, HandlerAdapter,ViewResolver,其中ViewResolver从实战的角度来说快没有用武之地了,至于为什么我们稍后再说。其中每一步我都标了序号,请求就是按照序号被一路处理过来的

SpringBoot的出现,使的我们开发一个web程序简单到令人发指。如果你使用过spring mvc下面的代码肯定是再熟悉不过了,加几个注解,然后一个API就写好了,但是你是否思考过其背后的执行流程呢?

@RestController
@RequestMapping("/mvc")
public class MvcController 

    @GetMapping("/obj")
    public ResponseEntity<MyResponse> getObj(@RequestParam("name") String name)
        MyResponse response = new MyResponse();
        response.setName(name);
        response.setAge(18);

        ResponseEntity<MyResponse> entity = new ResponseEntity<>(response, HttpStatus.OK);
        return entity;
    

  • HandlerMapping

通过HandlerMapping将我们写的Controller类(使用@Controller 或者@RequestMapping标记的类)里面带有@RequestMaping(包括各种变种,例如@GetMapping,@PostMapping等等)标记的方法以及拦截器(实现了HandlerInterceptor接口的类)包装成 HandlerExecutionChain返回给DispatherServlet

说白了,就是找到你写的那些处理器方法和拦截器,然后定位到你要请求的那个。由于SpringBoot的流行,我们几乎所有时间都在使用注解,所以最重要的一个实现类是:RequestMappingHandlerMapping。 这个很好记拉,名称前面那个RequestMapping就是你写Controller时候经常要加的@RequestMapping注解。

下面是其类继承结构图。

  • HandlerAdapter

HandlerMapping把处理器和拦截器都处理好了,把定位到的处理器和拦截器包装成HandlerExecutionChain对象返回给了DispatherServlet,接下来就是要具体执行里面的处理器和拦截器的逻辑了。这货非常重要,具体的执行逻辑,以及执行结果的转换都由它来负责,最后返回一个ModelAndView实例给DispatherServlet

由于注解的流行,我们经常使用的一个HandlerAdapter为RequestMappingHandlerAdapter。 这个也很好记拉,名称前面那个RequestMapping就是你写Controller时候经常要加的@RequestMapping注解。

其类继承结构图如下:

  • ViewResolver

这个组件主要是负责将逻辑视图解析为具体的视图。

到这块是不是又懵逼了,啥是逻辑视图?这块不懂也不能怪你,接触Jave web编程晚的同学可能都不知道啥是JSP了吧?现在前后端分离基本已经成为行业标准,所以也很少有人写jsp,以及使用其他模板引擎写网页了。以前web开发html都是后端写的,然后返给浏览器来渲染的,现在这种做法已经绝迹了吧?那个逻辑视图你就理解为一个指向你写的一个类似html文件的路径字符串好啦,ViewResolver就负责通过这个字符串找到你写的那个文件,然后结合数据将那个文件填充为html文件返回给前端。

这块不明白也没关系拉,现在都2022年了,这玩意在实践中基本用不上了,我们平时都是返回JSON,根本就走不到这一步,不过面试的时候有可能会用…

源码浅析

上面讲了个大概,我们稍微分析下源码吧,验证一下上面的流程

注册控制器

SpringMvc在启动的的时候就会把你写的那些Controller类(@Controller或者@RequestMapping标记)都给保存好。

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean


//由于其实现了InitializingBean接口,afterPropertiesSet()方法会自动执行。启动处理handlerMathods的方法。

@Override
public void afterPropertiesSet() 
	initHandlerMethods();


//在initHandlerMethods()方法中,从所有的bean里过滤出使用@Controller或者@RequestMapping标记的类,然后处理它里面的那些处使用@RequestMapping标记的理器方法。
for (String beanName : getCandidateBeanNames()) 
	if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) 
		processCandidateBean(beanName);
	


//例如我们写的这个例子中,此处的beanName = "mvcController"
protected void processCandidateBean(String beanName) 
	Class<?> beanType = obtainApplicationContext().getType(beanName);
	...
	//isHandler  判断MvcController是否被@Controller或者@RequestMapping标记
	if (beanType != null && isHandler(beanType)) 
	     //从handler中过滤出request方法,
		detectHandlerMethods(beanName);
	


//在detectHandlerMethods方法中通过RequestMappingHandlerMapping的
RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType)
方法将那些处理器方法转化为RequestMappingInfo对象,

最后,通过registerHandlerMethod(handler, invocableMethod, mapping);将其保存到 AbstractHandlerMethodMapping的

private final MappingRegistry mappingRegistry = new MappingRegistry();里。

所以,程序启动后,你写的那些Controller里的处理器方法都已经以RequestMappingInfo的形式被注册到一个叫MappingRegistry的容器里面去了,等请求过来的时候就会从这里面取。

执行请求

首次调用的时候,会执行DispatcherServlet的initStrategies方法,这个方法用来获取各种组件,其中就包括我们的3大组件。

protected void initStrategies(ApplicationContext context) 
    ...
    initHandlerMappings(context);
	...
	initHandlerAdapters(context);
    ...
	initViewResolvers(context);

其中handlerMapping被保存到下面这个list中

private List<HandlerMapping> handlerMappings;

包括RequestMappingHandlerMapping的实例,

其中HandlerAdapter被保存到下面这个list中

private List<HandlerAdapter> handlerAdapters;

包括RequestMappingHandlerAdapter的实例

然后调用了DispatcherServlet的 doDispatch方法,这个方法非常重要,是整个请求执行流程的脉络。

注意:非初次调用接口不走初始化方法,直接走doDispatch方法。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response)
    //将HandlerMethod 和 intercetor 转化为HandlerExecutionChain
    HandlerExecutionChain mappedHandler = getHandler(processedRequest);
    
    //获取HandlerAdapter,例如RequestMappingHandlerAdapter
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
    //执行拦截器前置方法
	if (!mappedHandler.applyPreHandle(processedRequest, response)) 
		return;
	
     
    //调用方法
    ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
    //拦截器后置方法
    mappedHandler.applyPostHandle(processedRequest, response, mv);
    
    //拦截器完成方法
	processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);


最后会调用RequestMappingHandlerAdapter的

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod)		
    ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
    ...
	invocableMethod.invokeAndHandle(webRequest, mavContainer);
	...
	return getModelAndView(mavContainer, modelFactory, webRequest);

获得一个ModelAndView对象,其中在invokeAndHandle中调用了 public class InvocableHandlerMethod extends HandlerMethod

protected Object doInvoke(Object... args) throws Exception 
	Method method = getBridgedMethod();
    return method.invoke(getBean(), args);
	...

可见最后还是使用了反射, 最终会进入AbstractMessageConverterMethodProcessor 类的

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
	ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
	throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException 

方法,方法调用的结果在这里进行转换,例如经常被使用MappingJackson2MessageConverter转换为json。

总结

以上就是Spring Mvc大概的执行流程,希望你了了…

SpringBoot之所以好用是因为其对整个流程做了高度抽象,如果不懂其中的原理那么它对于我们来说就是一个黑盒,对于日常搬砖是没什么问题,一旦出了bug就抓瞎了…而且只有掌握了其原理,才能更好的使用它。

一如既往的你可以从GitHub上获得本文源码:mvc-inspect

以上是关于秒懂SpringBoot之Mvc请求执行流程浅谈的主要内容,如果未能解决你的问题,请参考以下文章

浅谈后端语言优缺点

秒懂SpringBoot之全网最易懂的Spring Security教程

秒懂SpringBoot之Filter与HandlerInterceptor异同

Spring MVC系列之Spring MVC的前端控制器(DispatcherServlet)工作流程及原理

浅谈Java五大设计原则之责任链模式

浅谈Tomcat接收到一个请求后在其内部的执行流程(源码)