SpringMVC 源码解析
图为网上别人的,只可以用来参考,最终真正的运行流程还是要通过自己的源码 debug 来进行理解。
完成 Handler - Controller 控制器的方式
- 使用注解方式
- 实现 Controller 接口
- 实现 HttpRequestHeader 接口
实现接口的方法是 BeanName 的方式,他们需要加上 @Component("/uri.html") 注解,并设置 URI 地址,BeanName 方式和 注解的 @GetMapping 和 @PostMapping 一样的效果,只是现在过时了,都用注解编码。
服务器加载时
服务器加载时会将当前的所有的 controller 请求以 key-value 方式放入 HanderMap 里去,key - uri ,value - controller对象,注解方式 -> 方法对象,beanName 方式 -> 类对象,两种方式不同,在映射器和适配器的方式也不同。
核心方法的入口
- FrameworkServlet.doget/dopost/…
- FrameworkServlet.processRequest
- FrameworkServlet.doService
- DispatcherServlet.doService
- DispatcherServlet.doDispatch
DispatcherServlet 继承了 FrameworkServlet,FrameworkServlet 是抽象类,DispatcherServlet 实现了doService 的方法体并且会拦截所有请求, doService 方法中调用了 doDispatch 方法,最终执行的 doDispatch 方法,才完成整个请求到响应的调度与解耦过程。
doDispatch 源码解析
两种处理器,三种适配器
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ps:先将方法中要使用的对象进行初始化
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);
// ps:结束后会得到一个执行链,mappedHandler(HandlerExecutionChain) 的类中聚合了 Handler 与 拦截器的数组与集合两个成员变量,Handler 是 Object 类型,因为它如果请求的需要执行的代码是 注解方式配置的,则是方法对象,如果是 实现 Controller 或者 HttpRequestHeader 的 BeanName 方式,存储的就是 类对象
// ps:在 getHandler 执行的方法中,使用策略模式,对 HandlerMappings 集合进行非 null 判断后,将 HandlerMappings 的集合进行 foreach 循环迭代,因为 HandlerMappings 集合里的 HandlerMapping 是接口,所以直接循环进行调用他们的 getHandler 方法完成多态的执行方式,对所有不同的 控制器进行了执行链的装配,并最终返回给 DispatcherServlet 的 doDispatch 的方法中
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 确定当前请求的处理程序适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// ps:这里的控制器适配器将执行链 Handler 取出进行适配的处理,采用了适配器模式,内部会循环规定好的 handlerAdapters 集合里的 handlerAdapter 对象进行迭代,handlerAdapter 也是接口与 HandlerMapping 一样,有不同的适配器对象进行了实现,循环对 Handler 处理器进行类型判断,然后返回一个适配器给 doDispatch 方法中
// Process last-modified header, if supported by the handler.
// 处理最后修改头,如果支持的处理程序
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler. 实际调用处理程序
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//ps:这里对获得到的具体适配器进行方法的调用执行,并将执行链里得到的 Handler 放入其中,最后会调用到我们自己书写的 Handler-Controller 的方法或者对象方法,内部会对我们的方法需要的参数,进行反射机制处理,也就是有个参数处理器来进行操作,判断类型后进行对应的类型转换,或者就是对类的成员属性赋值操作,最终在调用我们方法时候将装配好的参数传给我们的方法中,最终以 mv - ModelAndView 类型对象返回回来到 doDispatch
//ps:参数处理器也是一种策略模式,简略了 很多 if...elseif 的操作判断
//ps:这里结束也就是方法的核心了,后面会根据 ModelAndView 进行视图的解析处理,然后对视图的数据渲染,最终响应给用户显示。
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
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.
//在4。3版本中,我们也在处理处理器方法抛出的错误,
//使它们可用于@ExceptionHandler方法和其他场景。
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);
}
}
}
}
流程总结
-
首先用户发送过来 URL 的请求,SpringMVC 的核心组件 DisPatchServlet 会拦截到所有的请求,因为在 SpringSSM 中的 web.xml 会配置 Servlet 是会设置 “/” 作为请求的地址,所以会拦截到用户所有的请求,如果是 SpringBoot 则内置了该配置,然后 DisPatchServlet 通过重写了 doService 方法,通过父类的执行调用机制 doget -> processRequest -> doService 得以调用,然后在 doService 还有一个 doDisPatch 方法的调用,也就是真正的核心方法,在这个方法里会调用相关的 HandlerMapping 与 HandlerAdapter 的相关方法的调用执行。
-
在 doDispatch 方法中开始会声明一些后面用到的局部变量,然后先执行 getHandler 的方法,getHandler 方法里其实就是采用的策略模式,先对 HandlerMappings 的集合进行非空判断,然后进行 foreach 循环遍历,这里在程序运行时候其实是会有两个mapping 映射器的实现类存在,一个是注解的方式,一个则是 BeanName 的方式,BeanName的方式是通过实现 Controller 和 实现 HttpRequestHander 接口进行的配置的 Handler, 再迭代时调用他们实现的接口 getHandler 方法,这里内部会对程序初始化时候装配的 HandlerMap 进行 key 的查找,key 为 request 里取出的 URI,值的话可能会是类对象也可能是方法对象,这个取决于你配置这个 URI 采用的注解方式还是BeanName 方法,每个映射器都会进行方法调用尝试获取,如果获取到里面装配好的 执行链不是 null ,则结束循环将得到的 执行链 HandlerExecutionChain 返回给 DisPatchServlet 的 doDispatch 方法中,执行链里依赖了一个 object 类型的 handler 成员属性,和分别是数组和集合的拦截器成员属性。
-
现在 DisPatchServlet 拥有了 HandlerExecutionChain 执行链后,因为执行链里的 handler 是 object 类型,可能是 类对象也可能是方法对象,所以需要合适的适配器对他进行解析具体的调用,这里会调用一个 getHandlerAdapter 方法,方法内部结构和获取映射器 HandlerMapping 的结构差不多,都是先对集合进行非空判断然后循环遍历接口的集合,这里的集合时适配器接口的集合,foeeach 循环的操作内部是将 handler 与各种适配器实现了类型的判断方法进行匹配,如果返回了 true 则将正确的适配器返回给 DisPatchServlet 的 doDispatch 方法中,方便后面的调用。
-
拥有了执行链与适配器后,就可以对具体我们书写的 Controller 也就是 Handler 方法进行调用了,通过适配器 ha.handle(processedRequest, response, mappedHandler.getHandler()) 进行调用,在内部会通过参数处理器对 request 请求体里的字符串参数,进行具体的类型判断和转换,或者对我们的 DOJO 对象进行反射的装配,最后传参的方式传给我们的方法体内进行具体的后端业务操作,最终会以 ModelAndView 的类型对象返回给 DisPatchServlet 的 doDispatch 中。
// 这是源码中的一段,这里是接口多态的方式进行调用的 // Actually invoke the handler. 实际调用处理程序 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
-
在调用 Controller 后,DisPatchServlet 有了最终需要响应的的 ModelAndView 的对象了,后面则是会对这个对象进行 视图解析器 得到一个粗犷的 view 视图,然后通过渲染器对 view 视图进行数据的渲染,并得到最终需要展示给用户的 view 视图,最后响应给用户。