文档章节

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

须臾之余
 须臾之余
发布于 08/02 07:50
字数 1370
阅读 23
收藏 0

DispatcherServlet源码分析

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

// 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在找到我们最后的子类

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

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函数,如下:

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原理

private List<HandlerMapping> handlerMappings;

mappedHandler = this.getHandler(processedRequest);

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

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

/**
 * 请求方法前置拦截,如果返回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);

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

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()方法

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

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

this.initHandlerMappings(context);

本文参考

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

蚂蚁课堂

http://www.mayikt.com/

 

© 著作权归作者所有

须臾之余
粉丝 125
博文 67
码字总数 178724
作品 0
吉安
程序员
私信 提问
springmvc源码解析合集

更多精彩源码解析文章请关注”天河聊架构“微信公众号。 springmvc源码解析之组件介绍 springmvc源码解析之配置加载SpringServletContainerInitializer springmvc源码解析之配置加载Context...

天河2018
03/27
202
0
浅谈SpringMVC执行过程

通过深入分析Spring源码,我们知道Spring框架包括大致六大模块, 如Web模块,数据库访问技术模块,面向切面模块,基础设施模块,核心容器模块和模块, 其中,在Spring框架的Web模块中,又包含...

编程SHA
03/08
47
0
Spring MVC 原理探秘 - 一个请求的旅行过程

1.简介 在前面的文章中,我较为详细的分析了 Spring IOC 和 AOP 部分的源码,并写成了文章。为了让我的 Spring 源码分析系列文章更为丰富一些,所以从本篇文章开始,我将来向大家介绍一下 Sp...

coolblog.xyz
2018/07/02
0
0
Spring源码分析之WebMVC

作者: 一字马胡 转载标志 【2018-01-07】 更新日志 导入 Spring源码分析系列文章索引: Spring源码分析之Bean的解析 Spring源码分析之Bean的加载 Spring源码分析之AOP 本文是系列文章的第四...

疼丸李白
2018/01/07
0
0
自己手写一个SpringMVC框架

前端框架很多,但没有一个框架称霸,后端框架现在Spring已经完成大一统。所以学习Spring是Java程序员的必修课。 Spring框架对于Java后端程序员来说再熟悉不过了,以前只知道它用的反射实现的...

yzbty23
01/30
108
0

没有更多内容

加载失败,请刷新页面

加载更多

聊聊nacos的LocalConfigInfoProcessor

序 本文主要研究一下nacos的LocalConfigInfoProcessor LocalConfigInfoProcessor nacos-1.1.3/client/src/main/java/com/alibaba/nacos/client/config/impl/LocalConfigInfoProcessor.java p......

go4it
昨天
4
0
前端技术之:webpack热模块替换(HMR)

第一步:安装HMR中间件: npm install --save-dev webpack-hot-middleware 第二步:webpack配置中引入webpack对象 const webpack = require('webpack’); 第三步:增加devServer配置项: ho......

popgis
昨天
4
0
死磕 java线程系列之线程池深入解析——体系结构

(手机横屏看源码更方便) 注:java源码分析部分如无特殊说明均基于 java8 版本。 简介 Java的线程池是块硬骨头,对线程池的源码做深入研究不仅能提高对Java整个并发编程的理解,也能提高自己...

彤哥读源码
昨天
4
0
虚函数表 图解

虚函数表 图解 p504

天王盖地虎626
昨天
3
0
java反射

学习目标  什么是反射  反射运行原理  了解反射机制的相关类  获取 class 对象的 3 种方式  通过反射获取构造方法并使用  通过反射获取成员变量并调用  通过反射获取成员方法并...

流川偑
昨天
4
2

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部