SpringMVC源码深度解析之DispatcherServlet源码分析

Easter79
• 阅读 440

SpringMVC源码深度解析之DispatcherServlet源码分析

DispatcherServlet源码分析

SpringMVC核心就是DispatcherServlet,所有得请求都会转发到DispatcherServlet,然后再通过DispatcherServlet执行具体得控制层(Handler)返回ModelAndView给客户端视图展示。

SpringMVC源码深度解析之DispatcherServlet源码分析

// 3. 将我们的DispatcherServlet 注入到 serlvet容器中 ServletRegistration.Dynamic dynamic = servletContext.addServlet("dispatcher", new DispatcherServlet(app)); // 4.填写url路径映射 dynamic.addMapping("/");

DispatcherServlet其实就是一个Servlet类,无非就是包装一层,通过url能够映射找到我们得SpringMvc中定义得请求方法。

源代码分析:

  1. 类的集成关系

DispatcherServlet继承FrameworkServlet继承HttpServlet

面向基本上思想 重写 先走父类 ,在走子类。

得出答案:先看HttpServlet在找到我们最后的子类

SpringMVC源码深度解析之DispatcherServlet源码分析

SpringMVC源码深度解析之DispatcherServlet源码分析

protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.processRequest(request, response); }

SpringMVC源码深度解析之DispatcherServlet源码分析

SpringMVC源码深度解析之DispatcherServlet源码分析

DispatcherServlet与Servlet关系

关系:DispatcherServlet继承FrameworkServlet继承HttpServlet

流程执行关系:

HttpServlet service方法 判断请求方法的类型

FrameworkServlet doService

DispatcherServlet doService

DispatcherServlet的初始化

在servlet初始化阶段会调用其init方法,所以我们首先要查看在DispatcherServlet中是否重写了init方法。我们在其父类HttpServletBean中找到该方法

public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware { .... public final void init() throws ServletException { if (this.logger.isDebugEnabled()) { this.logger.debug("Initializing servlet '" + this.getServletName() + "'"); }        //解析init-param并封装至pvs中 PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try {                //将当前的servlet类转换为一个BeanWrapper,从而能够以Spring的方式来对init-param的值进行注入 BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());               //注册自定义属性编辑器,一旦遇到Resource类型的属性会使用ResourceEditor进行解析 bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));                 //空实现,留给子类覆盖 this.initBeanWrapper(bw);                 //属性注入 bw.setPropertyValues(pvs, true); } catch (BeansException var4) { if (this.logger.isErrorEnabled()) { this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4); }

            throw var4;
        }
    }

       //留给子类扩展 this.initServletBean(); if (this.logger.isDebugEnabled()) { this.logger.debug("Servlet '" + this.getServletName() + "' configured successfully"); }

}

.... }

DispatcherServlet的初始化过程主要是通过将当前的Servlet类型实例转换为BeanWrapper类型实例,以便使用Spring中提供的注入功能进行对应属性的注入。

我们看下servletBean的初始化,HttpServletBean其父类FrameworkServlet覆盖了它的initServletBean函数,如下:

SpringMVC源码深度解析之DispatcherServlet源码分析

protected final void initServletBean() throws ServletException { this.getServletContext().log("Initializing Spring FrameworkServlet '" + this.getServletName() + "'"); if (this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization started"); }     //计时器,统计初始化的执行时间 long startTime = System.currentTimeMillis();

try {

        //关键的初始化逻辑委托给了这个方法 this.webApplicationContext = this.initWebApplicationContext();         //设计为子类覆盖 this.initFrameworkServlet(); } catch (RuntimeException | ServletException var5) { this.logger.error("Context initialization failed", var5); throw var5; }

if (this.logger.isInfoEnabled()) {
    long elapsedTime = System.currentTimeMillis() - startTime;
    this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization completed in " + elapsedTime + " ms");
}

}

WebApplicationContext 的初始化

initWebApplicationContext函数主要工作就是创建或者刷新****WebApplicationContext 实例并对servlet功能所使用的变量进行初始化

protected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) {        //context实例在构造函数中被注入 wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac; if (!cwac.isActive()) { if (cwac.getParent() == null) { cwac.setParent(rootContext); }                //刷新上下文环境 this.configureAndRefreshWebApplicationContext(cwac); } } }

if (wac == null) {

        //根据contextAttribute属性加载webApplicationContext wac = this.findWebApplicationContext(); }

if (wac == null) {
    wac = this.**createWebApplicationContext**(rootContext);
}

if (!this.refreshEventReceived) {
    this.**onRefresh**(wac);
}

if (this.publishContext) {
    String attrName = this.getServletContextAttributeName();
    this.getServletContext().setAttribute(attrName, wac);
    if (this.logger.isDebugEnabled()) {
        this.logger.debug("Published WebApplicationContext of servlet '" + this.getServletName() + "' as ServletContext attribute with name \[" + attrName + "\]");
    }
}

return wac;

}

刷新方法onRefresh

protected void onRefresh(ApplicationContext context) { this.initStrategies(context); }

protected void initStrategies(ApplicationContext context) {

initMultipartResolver(context); //初始化上传文件解析器(或者是多部分请求解析器)

initLocaleResolver(context);//初始化本地化解析器

initThemeResolver(context);//初始化主题解析器

initHandlerMappings(context);//初始化处理器映射器

initHandlerAdapters(context);//初始化处理器适配器

initHandlerExceptionResolvers(context);//初始化处理器异常解析器

initRequestToViewNameTranslator(context);//初始化请求到视图名翻译器

initViewResolvers(context);//初始化视图解析器

initFlashMapManager(context);//初始化重定向数据管理器

DispatcherServlet的逻辑处理

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { ... try { try { ...                 //通过url路径地址去查找控制层类方法,如果没有找到的化,直接返回404 mappedHandler = this.getHandler(processedRequest);         .... HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler()); String method = request.getMethod(); boolean isGet = "GET".equals(method); .... if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; }

            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
      ....
            mappedHandler.applyPostHandle(processedRequest, response, mv);

.... }

SpringMVC源码之定位Handler原理

SpringMVC源码深度解析之DispatcherServlet源码分析

private List handlerMappings;

SpringMVC源码深度解析之DispatcherServlet源码分析

mappedHandler = this.getHandler(processedRequest);

SpringMVC源码深度解析之DispatcherServlet源码分析

SpringMVC源码深度解析之DispatcherServlet源码分析

HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());

SpringMVC源码深度解析之DispatcherServlet源码分析

if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; }

SpringMVC源码深度解析之DispatcherServlet源码分析

/** * 请求方法前置拦截,如果返回true 表示会执行到目标方法(请求方法) 如果返回false的情况下 则不会执行目标方法。 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getParameter("token"); System.out.println(">>>>token<<<<:" + token); if (StringUtils.isEmpty(token)) { response.setStatus(500); response.getWriter().print(" token is null"); return false; } // 执行我们的请求方法 return true; }

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

执行目标方法:

@RequestMapping("/pageIndex") public String pageIndex() { System.out.println(">>>pageIndex<<<<"); return "pageIndex"; }

mappedHandler.applyPostHandle(processedRequest, response, mv);

SpringMVC源码深度解析之DispatcherServlet源码分析

@Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("<<>>>"); // 请求之后执行。 }

DispatcherServlet源码流程分析

1.执行doDispatch

2.调用getHandler方法获取请求目标的方法  也就是  请求url映射路径对应的控制层具体的方法

handlerMappings的作用查找控制器位置,比如xml和注解方式。

3.调用getHandlerAdapter获取控制层适配器 RequestMappingHandlerAdapter

4.执行拦截器前置方法 preHandle() 如果返回为true的话

5.执行实际请求目标方法 返回modeAndView对象

6.执行拦截器PostHandle()方法

7.设置渲染视图层内容

8.执行拦截器afterCompletion方

SpringMVC控制层容器初始化

  1. HttpServletBean  init ()方法
  2. FrameworkServlet initServletBean方法→  initWebApplicationContext();
  3. DispatcherServlet onRefresh方法→  initStrategies()方法

SpringMVC源码深度解析之DispatcherServlet源码分析

protected void onRefresh(ApplicationContext context) { this.initStrategies(context); }

当我们servlet容器初始化的时候初始化

this.initHandlerMappings(context);

SpringMVC源码深度解析之DispatcherServlet源码分析

本文参考

参考数据:Spring源码深度解析

蚂蚁课堂

http://www.mayikt.com/

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
2年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k