前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringMVC——DispatcherServlet的IoC容器(Web应用的IoC容器的子容器)创建过程

SpringMVC——DispatcherServlet的IoC容器(Web应用的IoC容器的子容器)创建过程

作者头像
用户1148394
发布2018-01-12 17:36:19
1K0
发布2018-01-12 17:36:19
举报
文章被收录于专栏:余林丰余林丰

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

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

代码语言:javascript
复制
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没有区别.

代码语言:javascript
复制
 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方法来实现我们自己的业务逻辑.

代码语言:javascript
复制
//GenericServlet.java
public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
}
...
public void init() throws ServletException {

}
...

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

代码语言:javascript
复制
//HttpServletBean.javapublic final void init() throws ServletException {
    if (logger.isDebugEnabled()) {
        logger.debug("Initializing servlet '" + getServletName() + "'");
    }

    // Set bean properties from init parameters.从初始化参数中设置bean属性
    try {
        PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
        BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
        ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
        bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
        initBeanWrapper(bw);
        bw.setPropertyValues(pvs, true);
    }
    catch (BeansException ex) {
        if (logger.isErrorEnabled()) {
            logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
        }
        throw ex;
    }

    // Let subclasses do whatever initialization they like.调用子类的initServletBean进行具体的初始化
    initServletBean();

    if (logger.isDebugEnabled()) {
        logger.debug("Servlet '" + getServletName() + "' configured successfully");
    }
}
...
//具体的初始化交由子类去完成,即FrameworkServlet
protected void initServletBean() throws ServletException {
}

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

代码语言:javascript
复制
//FrameServlet.java
protected final void initServletBean() throws ServletException {
    getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
    if (this.logger.isInfoEnabled()) {
        this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
    }
    long startTime = System.currentTimeMillis();

    try {
        this.webApplicationContext = initWebApplicationContext();   //在这里不是初始化Spring根应用上下文(Web应用的IoC容器),而是初始化SpringMVC的Servlet上下文创建自己所持有的IoC容器.如果没有则调用createWebApplicationContext方法进行创建.并将根应用上下文作为它的双亲上下文
        initFrameworkServlet(); //此方法也没有给出具体实现,再其子类DispatcherServlet也没有对它重写.
    }
    catch (ServletException ex) {
        this.logger.error("Context initialization failed", ex);
        throw ex;
    }
    catch (RuntimeException ex) {
        this.logger.error("Context initialization failed", ex);
        throw ex;
    }

    if (this.logger.isInfoEnabled()) {
        long elapsedTime = System.currentTimeMillis() - startTime;
        this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
                elapsedTime + " ms");
    }
}
...
//在所有的bean配置参数和WebApplicationContext被加载后会调用此方法,默认实现为空,它的子类可以重写此方法来实现需要的初始化操作.子类DispatcherServlet并没有重写.
protected void initFrameworkServlet() throws ServletException {
}

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

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

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

代码语言:javascript
复制
//DispatcherServlet.java
protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context);
    initLocaleResolver(context);
    initThemeResolver(context);
    initHandlerMappings(context);    //这里就是为http请求找到相应的Controller控制器
    initHandlerAdapters(context);
    initHandlerExceptionResolvers(context);
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);
    initFlashMapManager(context);
}

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

代码语言:javascript
复制
//FrameworkServlet.java
protected WebApplicationContext initWebApplicationContext() {
    ......
    if (this.webApplicationContext != null) {
        ......
        configureAndRefreshWebApplicationContext(cwac);     //在此方法中调用的onRefresh
        ......
    }
    if (wac == null) {
        wac = createWebApplicationContext(rootContext);     //此方法中最后也是调用的configureAndRefreshWebApplicationContext方法
    }

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

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-05-04 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档