秒懂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异同