在上一篇《Spring——Web应用中的IoC容器创建(WebApplicationContext根应用上下文的创建过程)》中说到了Web应用中的IoC容器创建过程.这一篇主要讲SpringMVC的核心DispatcherServlet.

从web.xml中简要回顾一下WebApplicationContext根应用上下文的创建过程.具体过程详见上篇博客.

  1.   <!--WebApplicationContext配置参数-->
  2.   <context-param>
  3.   <param-name>contextConfigLocation</param-name>
  4.   <param-value>classpath*:applicationContext.xml</param-value>
  5.   </context-param>
  6. <!--注册ContextLoaderListener,加载根应用上下文-->
  7.   <listener>
  8.   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  9.   </listener>

DispatcherServlet实际上就是一个Servlet所以它在web.xml中的配置和普通的servlet没有区别.

  1. <!--注册DispatcherServlet,加载应用上下文-->
  2. <servlet>
  3. <servlet-name>springmvc</servlet-name>
  4. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  5. <init-param>
  6. <param-name>contextConfigLocation</param-name>
  7. <param-value>classpath*:spring-servlet.xml</param-value> <!--若不显示添加配置文件路径,则会默认加载servlt-name的名字+"-servlet.xml"-->
  8. </init-param>
  9. <load-on-startup>1</load-on-startup>
  10. </servlet>
  11. <!--servlet映射-->
  12. <servlet-mapping>
  13. <servlet-name>springmvc</servlet-name>
  14. <url-pattern>/</url-pattern>
  15. </servlet-mapping>

用过原生Servlet写过web都知道自定义的Servlet需要继承HttpServlet类实现doPost和doGet方法.DispatcherServlet类的主要继承关系如下:

在这篇博客中从不细讲Servlet,从HttpServletBean的开始讲起.


DispatcherServlet是什么?它为什么在SpringMVC中起到核心作用?原因很简单:所有来自客户端的请求都会经过DispatcherServlet,由DispatcherServlet将不同的请求分发至不同的Controller,所以DispatcherServlet是一个前置控制器起的是分发来自客户端请求的作用.根据不同的配置会接收不同的请求,这在web.xml中servlet映射中可体现.如果配置的是"/"则是所有请求都会经过DispatcherServlet,但通常不会这么做,比如一些静态资源就不必经过DispatcherServlet.

首先大致了解一下Servlet.Web容器接收到来自客户端不同类型(post,get等)的时候,实际上是所有的请求都是访问Servlet接口的service方法,在HttpServlet抽象类中实现了service方法,在service方法中判断是哪种具体的请求,再将不同的请求分发至不同的处理方法.

用原生的Servlet编写的Web应用通常是继承HttpServlet方法,重写doGet和doPost方法.由于DispatcherServlet在SpringMVC中责任重大,作为一个前端控制器,所有的Web请求都需要通过它处理,进行转发,匹配,数据处理后,并转由页面进行展现.可以看到DispatcherServlet并没有直接继承HttpServlet,而是HttpServletBean.在Servlet初始化过程中,Servlet的init方法会被调用,而Servlet提供的API中init方法没有做任何事,也就是说我们可以通过重写init方法来实现我们自己的业务逻辑.

  1. //GenericServlet.java
  2. public void init(ServletConfig config) throws ServletException {
  3. this.config = config;
  4. this.init();
  5. }
  6. ...
  7. public void init() throws ServletException {
  8.  
  9. }
  10. ...

在HttpServletBean重写了init方法,并且不能被其子类所重写.

  1. //HttpServletBean.java
    public final void init() throws ServletException {
  2. if (logger.isDebugEnabled()) {
  3. logger.debug("Initializing servlet '" + getServletName() + "'");
  4. }
  5.  
  6. // Set bean properties from init parameters.从初始化参数中设置bean属性
  7. try {
  8. PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
  9. BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
  10. ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
  11. bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
  12. initBeanWrapper(bw);
  13. bw.setPropertyValues(pvs, true);
  14. }
  15. catch (BeansException ex) {
  16. if (logger.isErrorEnabled()) {
  17. logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
  18. }
  19. throw ex;
  20. }
  21.  
  22. // Let subclasses do whatever initialization they like.调用子类的initServletBean进行具体的初始化
  23. initServletBean();
  24.  
  25. if (logger.isDebugEnabled()) {
  26. logger.debug("Servlet '" + getServletName() + "' configured successfully");
  27. }
  28. }
  29. ...
  30. //具体的初始化交由子类去完成,即FrameworkServlet
  31. protected void initServletBean() throws ServletException {
  32. }

顺着初始化这条线我们来到FrameworkServlet.照猫画虎,它重写了父类的initServletBean,但同样将它置为不能被其子类所重写.

  1. //FrameServlet.java
  2. protected final void initServletBean() throws ServletException {
  3. getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
  4. if (this.logger.isInfoEnabled()) {
  5. this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
  6. }
  7. long startTime = System.currentTimeMillis();
  8.  
  9. try {
  10. this.webApplicationContext = initWebApplicationContext(); //在这里不是初始化Spring根应用上下文(Web应用的IoC容器),而是初始化SpringMVC的Servlet上下文创建自己所持有的IoC容器.如果没有则调用createWebApplicationContext方法进行创建.并将根应用上下文作为它的双亲上下文
  11. initFrameworkServlet(); //此方法也没有给出具体实现,再其子类DispatcherServlet也没有对它重写.
  12. }
  13. catch (ServletException ex) {
  14. this.logger.error("Context initialization failed", ex);
  15. throw ex;
  16. }
  17. catch (RuntimeException ex) {
  18. this.logger.error("Context initialization failed", ex);
  19. throw ex;
  20. }
  21.  
  22. if (this.logger.isInfoEnabled()) {
  23. long elapsedTime = System.currentTimeMillis() - startTime;
  24. this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
  25. elapsedTime + " ms");
  26. }
  27. }
  28. ...
  29. //在所有的bean配置参数和WebApplicationContext被加载后会调用此方法,默认实现为空,它的子类可以重写此方法来实现需要的初始化操作.子类DispatcherServlet并没有重写.
  30. protected void initFrameworkServlet() throws ServletException {
  31. }

简单回顾一下整个初始化过程(一个不规范的图)


以上部分只是简要的说明了一下DispatcherServlet的IoC容器初始化过程,但还是没有说明一个请求是如何在DispatcherServlet做到分发到不同Controller的.

在DispatcherServlet类中有一个initStrategies方法,在这个方法中初始化整个SpringMVC框架的初始化,包括其中的http请求映射关系:

  1. //DispatcherServlet.java
  2. protected void initStrategies(ApplicationContext context) {
  3. initMultipartResolver(context);
  4. initLocaleResolver(context);
  5. initThemeResolver(context);
  6. initHandlerMappings(context); //这里就是为http请求找到相应的Controller控制器
  7. initHandlerAdapters(context);
  8. initHandlerExceptionResolvers(context);
  9. initRequestToViewNameTranslator(context);
  10. initViewResolvers(context);
  11. initFlashMapManager(context);
  12. }

容易得知,initStrategies方法是在onRefresh方法中调用的,FrameworkServlet没有对onRefresh做任何有意义的实现,而是交由它的子类DispatcherServlet去完成.在FramworkServlet的initWebApplicationContext方法中完成了对它的调用.所以再次回到FramworkServlet的initWebApplicationContext方法,只截取其中一段:

  1. //FrameworkServlet.java
  2. protected WebApplicationContext initWebApplicationContext() {
  3. ......
  4. if (this.webApplicationContext != null) {
  5. ......
  6. configureAndRefreshWebApplicationContext(cwac); //在此方法中调用的onRefresh
  7. ......
  8. }
  9. if (wac == null) {
  10. wac = createWebApplicationContext(rootContext); //此方法中最后也是调用的configureAndRefreshWebApplicationContext方法
  11. }
  12.  
  13. if (!this.refreshEventReceived) {
  14. onRefresh(wac);
  15. }
  16. ......
  17. return wac;
  18. }

更为具体的SpringMVC处理http分发请求,我们再下一篇中再来详细讲解initStrategies中的initHandlerMappings.

SpringMVC——DispatcherServlet的IoC容器(Web应用的IoC容器的子容器)创建过程的更多相关文章

  1. Spring父容器与子容器

    在使用spring+springMVC的web工程中,我们一般会在web.xml中做如下配置: <context-param> <param-name>contextConfi ...

  2. spring之:XmlWebApplicationContext作为Spring Web应用的IoC容器,实例化和加载Bean的过程

    它既是 DispatcherServlet 的 (WebApplicationContext)默认策略,又是 ContextLoaderListener 创建 root WebApplicationC ...

  3. SpringMVC DispatcherServlet 说明与web配置

    使用Spring MVC,配置DispatcherServlet是第一步. DispatcherServlet是一个Servlet,所以能够配置多个DispatcherServlet. Dispatc ...

  4. 这一次搞懂Spring Web零xml配置原理以及父子容器关系

    前言 在使用Spring和SpringMVC的老版本进行开发时,我们需要配置很多的xml文件,非常的繁琐,总是让用户自行选择配置也是非常不好的.基于约定大于配置的规定,Spring提供了很多注解帮助我 ...

  5. springmvc.xml,context.xml和web.xml

    1:springmvc.xml配置要点 一般它主要配置Controller的组件扫描器和视图解析器 下为:springmvc.xml文件 <?xml version="1.0" ...

  6. java maven、springmvc、mybatis 搭建简单Web项目学习笔记

    前言: 空余的时间,学学 Java,没准哪天用的到: 环境搭建折腾了好几天,总算搞顺了,也做个学习笔记,以防后面会忘记: 一.安装文件及介绍 JDK:jdk1.8.0 77 eclipse-maven ...

  7. 【Spring】非Spring IOC容器下获取Spring IOC上下文的环境

    前言 在Spring Web项目中,有些特殊的时候需要在非Spring IOC容器下获取Spring IOC容器的上下文环境,比如获取某个bean. 版本说明 声明POM文件,指定需引入的JAR. & ...

  8. 对spring web启动时IOC源码研究

    研究IOC首先创建一个简单的web项目,在web.xml中我们都会加上这么一句 <context-param> <param-name>contextConfigLocatio ...

  9. spring-framework-中文文档一:IoC容器、介绍Spring IoC容器和bean

    5. IoC容器 5.1介绍Spring IoC容器和bean 5.2容器概述 本章介绍Spring Framework实现控制反转(IoC)[1]原理.IoC也被称为依赖注入(DI).它是一个过程, ...

随机推荐

  1. IOS开发创建开发证书及发布App应用(二)——创建证书

    2. 创建证书 证书分为两种,一种是开发者证书,主要是用来真机调试的 另一种就是发布证书,就是用来发布应用的, 最好是两种都要下载,不然编译时候可能报错,我猜想可能苹果怕你没用真机调试 创建证书分为两 ...

  2. HTML5 的WebSocket

    认识HTML5的WebSocket

  3. 项目在App Store的展示信息

    一.首部1.图标作用:一个软件的logo.修改:每次提交新版本时可以修改.要求:1>1024*1024像素 2>72dpi.RGB.平展.不透明.没有圆角 3>高品质的JPEG或PN ...

  4. 《c#入门经典第五版》简介及pdf电子书网盘下载地址(收藏)

    <C#入门经典(第5版)>全面讲解C# 2010和.net架构编程知识,为您编写卓越C# 2010程序奠定坚实基础.C#入门经典系列是屡获殊荣的C#名著和超级畅销书.最新版的<C#入 ...

  5. BS结构中,web如何将数据进行DES加密并写道IC卡中

    在IC卡应用系统中,一般都要对IC卡数据进行DES加密,以保证数据的安全.友我科技RFID读写器云服务2.0充分考虑了这个需求,只需要软件工程师简单的配置即可实现数据的加解密并且写到数据块中.如下图所 ...

  6. 使用PCA + KNN对MNIST数据集进行手写数字识别

    首先引入需要的包 %matplotlib inline import numpy as np import scipy as sp import pandas as pd import matplot ...

  7. OpenCV畸变校正原理以及损失有效像素原理分析

    上一篇博客简要介绍了一下常用的张正友标定法的流程,其中获取了摄像机的内参矩阵K,和畸变系数D. 1.在普通相机cv模型中,畸变系数主要有下面几个:(k1; k2; p1; p2[; k3[; k4; ...

  8. java复习(5)---接口、继承、多态

    Java作为完全面向对象语言,接口.继承和多态是三个非常重要的概念. 1.继承. (1)关键字: extends (2)子类用super()调用父类构造函数,用super().方法 调用父类的成员方法 ...

  9. 浩哥解析MyBatis源码(十二)——binding绑定模块之MapperRegisty

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6758456.html 1.回顾 之前解析了解析模块parsing,其实所谓的解析模块就是为 ...

  10. MySQL之改_update

    MySQL增删改查之改_update UPDATE语句 进行数据记录的更新. 1.更新单个表中的值 语法: UPDATE [IGNORE] table_reference SET col_name1= ...