Springmvc 横向源码原理解析(原创)
1.springmvc的基本流程(不多赘述)

2.主要涉及到的类

//该方法返回HandlerExecutionChain 类 并不是直接返回handler 是因为在HandlerExecutionChain中还有拦截器 preHandle(true执行后面的方法) postHandle afterCompletion 三个方法
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
在DispatcherServlet类中通过getHandler方法去遍历我们配置文件中写入的handlerMapping 从而找到我们的handle handle会有多种形态(Control HttpRequest Servlet RequestMapping) 此时需要我们的handleadapter去适配
在doDisdapch方法中 通过handle去找对应的适配器
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 || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 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 (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
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());
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.
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);
}
}
}
}
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {//去检测有没有拦截器 有的话返回true 没有的话返回false 且后面的拦截器方法都不执行
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
通过上面红色的部分 进入getHandlerAdapter
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
通过对handlerAdapters遍历的方法去找到适配的handler 找到后基于HandlerAdapter中的handler方法返回我们的ModelAndView
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
接收到返回ModelAndView以后 再去找DispatcherServlet类中的resolveViewName方法
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
HttpServletRequest request) throws Exception { for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
然后通过我们的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.resolveLocale(request);
response.setLocale(locale);
View view;
if (mv.isReference()) {
// We need to resolve the view name.
view = resolveViewName(mv.getViewName(), 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() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isDebugEnabled()) {
logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
getServletName() + "'", ex);
}
throw ex;
}
}

HandlerMapping详解

目前主流的三种mapping 如下:
BeanNameUrlHandlerMapping: 基于ioc name 中已 "/" 开头的Bean时行 注册至映谢.
SimpleUrlHandlerMapping:基于手动配置 url 与control 映谢
RequestMappingHandlerMapping:基于@RequestMapping注解配置对应映谢
HandlerAdapter详解
这里spring mvc 采用适配器模式来适配调用指定Handler,根据Handler的不同种类采用不同的Adapter,其Handler与 HandlerAdapter 对应关系如下:
|
Handler类别 |
对应适配器 |
描述 |
|
Controller |
SimpleControllerHandlerAdapter |
标准控制器,返回ModelAndView |
|
HttpRequestHandler |
HttpRequestHandlerAdapter |
业务自行处理 请求,不需要通过modelAndView 转到视图 |
|
Servlet |
SimpleServletHandlerAdapter |
基于标准的servlet 处理 |
|
HandlerMethod |
RequestMappingHandlerAdapter |
基于@requestMapping对应方法处理 |
HandlerAdapter 接口方法


ViewResolver 与View 详解
找到应的Adapter 之后就会基于适配器调用业务处理,处理完之后业务方会返回一个ModelAndView ,在去查找对应的视图进行处理。其在org.springframework.web.servlet.DispatcherServlet#resolveViewName() 中遍历 viewResolvers 列表查找,如果找不到就会报一个 Could not resolve view with name 异常。

在下一步就是基于ViewResolver.resolveViewName() 获取对应View来解析生成Html并返回 。对应VIEW结构如下:

Springmvc 横向源码原理解析(原创)的更多相关文章
- JUC 并发编程--11, AQS源码原理解析, ReentrantLock 源码解读
这里引用别人博客,不重复造轮子 https://blog.csdn.net/u012881584/article/details/105886486 https://www.cnblogs.com/w ...
- mybatis 3.x源码深度解析与最佳实践(最完整原创)
mybatis 3.x源码深度解析与最佳实践 1 环境准备 1.1 mybatis介绍以及框架源码的学习目标 1.2 本系列源码解析的方式 1.3 环境搭建 1.4 从Hello World开始 2 ...
- 源码深度解析SpringMvc请求运行机制(转)
源码深度解析SpringMvc请求运行机制 本文依赖的是springmvc4.0.5.RELEASE,通过源码深度解析了解springMvc的请求运行机制.通过源码我们可以知道从客户端发送一个URL请 ...
- SpringMVC 源码深度解析<context:component-scan>(扫描和注冊的注解Bean)
我们在SpringMVC开发项目中,有的用注解和XML配置Bean,这两种都各有自己的优势,数据源配置比較经经常使用XML配置.控制层依赖的service比較经经常使用注解等(在部署时比較不会改变的) ...
- 并发编程(十五)——定时器 ScheduledThreadPoolExecutor 实现原理与源码深度解析
在上一篇线程池的文章<并发编程(十一)—— Java 线程池 实现原理与源码深度解析(一)>中从ThreadPoolExecutor源码分析了其运行机制.限于篇幅,留下了Scheduled ...
- 并发编程(十二)—— Java 线程池 实现原理与源码深度解析 之 submit 方法 (二)
在上一篇<并发编程(十一)—— Java 线程池 实现原理与源码深度解析(一)>中提到了线程池ThreadPoolExecutor的原理以及它的execute方法.这篇文章是接着上一篇文章 ...
- Thrift之代码生成器Compiler原理及源码详细解析1
我的新浪微博:http://weibo.com/freshairbrucewoo. 欢迎大家相互交流,共同提高技术. 又很久没有写博客了,最近忙着研究GlusterFS,本来周末打算写几篇博客的,但是 ...
- spring5 源码深度解析----- 被面试官给虐懵了,竟然是因为我不懂@Configuration配置类及@Bean的原理
@Configuration注解提供了全新的bean创建方式.最初spring通过xml配置文件初始化bean并完成依赖注入工作.从spring3.0开始,在spring framework模块中提供 ...
- 深入理解NIO(三)—— NIO原理及部分源码的解析
深入理解NIO(三)—— NIO原理及部分源码的解析 欢迎回到淦™的源码看爆系列 在看完前面两个系列之后,相信大家对NIO也有了一定的理解,接下来我们就来深入源码去解读它,我这里的是OpenJDK-8 ...
随机推荐
- Unity Tiny & ECS 学习笔记
1.官方文档 https://docs.unity3d.com/Packages/com.unity.tiny@0.13/manual/intro-for-unity-developers.html ...
- 线段树——习题、lazy解析
习题: C. Cloud Computing lazy操作解析:
- JavaScript在div后添加删除div
var idd = 'str'; $('.task-done-detail-content-p7').click(function () { var id = this.id; if(idd !== ...
- ES7的async/await
async 表示这是一个async函数,await只能用在这个函数里面. await 表示在这里等待promise返回结果了,再继续执行. await 后面跟着的应该是一个promise对象 awai ...
- axios formData提交数据 && axios设置charset无效???
但是这样会出现一个问题,什么问题呢? 我设置了请求头编码utf-8,但是没生效 content-type里面没有出现utf-8???????查了很多资料,说这是axios固有的bug,我....... ...
- 1、Altium Designer 入门
一.新建工程 File-->new-->Project-->newPCB Project 1.添加原理图 在Project面板选中项目,右键Add New to Project--& ...
- JAVA学习笔记(2)—— java初始化三个原则
1. 初始化原则 (1) 静态对象(变量)优先于非静态对象(变量)初始化,其中静态对象(变量)初始化一次,非静态对象(变量)可能会初始化多次. (2) 父类优先于子类初始化 (3) 按照成 ...
- Linux系统(虚拟机)安装禅道
1.查看linux系统版本 uname -a 2.禅道下载:http://www.zentao.net/download.html,找到要下载的版本,点击进入各平台下载: 3.将下载好的安装包上传到l ...
- C# 关于X86/X64/AnyCpu 的关系
电脑硬件CPU可以分为x86与x64, x86的机器只能安装32位的操作系统,如XP, WIN7_86, x64的机器既可以安装32位的系统,又可以安装64位的系统,只是在x64的机器上安装32位的系 ...
- spring-cloud-feign负载均衡组件
Feign简介: Feign是一个声明式的Web服务客户端,使用Feign可使得Web服务客户端的写入更加方便.它具有可插拔注释支持,包括Feign注解和JAX-RS注解.Feign还支持可插拔编码器 ...