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 ...
随机推荐
- Oracle ORA-08104报错处理方法及注意事项
[环境介绍] 系统环境:IBM P740 8205-E6C (AIX) + 11.2.0.3.0 Oracle RAC [背景介绍] 故障描述:数据库表空间超过90%,无法进行扩容表空间,需要业务侧清 ...
- java ,用公司框架写的显示列表 Table控件
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org" lan ...
- ARIMA模型原理
一.时间序列分析 北京每年每个月旅客的人数,上海飞往北京每年的游客人数等类似这种顾客数.访问量.股价等都是时间序列数据.这些数据会随着时间变化而变化.时间序列数据的特点是数据会随时间的变化而变化. 随 ...
- js关联数组
标准javascript 是内含支持hash关联数组,经查找资料并测试,有关标准javascript内含的hash关联数组操作备忘如下 1.Hash关联数组定义 // 定义空数组 myhash = { ...
- 第31月 第9天 责任链AppDelegate
1.AppDelegate 到这里我们把解决方案再明确一下:把 AppDelegate 的调用解耦成一个 责任链 模型.并且满足以下特征: 每个模块都可以无限制的实现 AppDelegate 的方法. ...
- Oracle 里 case 和decode的简单用法
case 就相想当于C#里面switch l 列:根据员工的职位,计算加薪后的薪水数据 如果职位是Analyst , 加薪10% 如果职位是Programmer 加薪5% 如果职位是clerk ...
- Tomcat 改端口
Tomcat 改端口 进入 tomcat 解压包下面的 conf 目录 打开文件 server.xml 找到以下 三处位置,并改为对应端口 1)找到 8005 <Server port=&quo ...
- find 命令局部小结之 xtime
大家在使用find命令的时候往往会使用它的 -name 或者 -xtime,在这里就说下他的xtime. find / -mtime +7 .find / -mtime -7.find / -mti ...
- 【Linux】常见基础命令之系统操作
linux现在基本上已成为面试的必考题目,特此总结一些常用的基础命令. cd:切换目录 lilip@ubuntu:~$ cd /home/lilip/test pwd:打印当前目录 lilip@ubu ...
- js 本地缓存localStorage
.localStorage - 没有时间限制的数据存储 ,,]; localStorage.setItem("stor",arr); console.log(localStorag ...