SpringMVC源码--DispatcherServlet的加载和初始化

    之前的文章介绍了Spring的初始化过程,Spring Web应用初始化依赖于Web容器的初始化,这在之前已经提到过了。那么初始化完成后,SpringMVC又是怎样发挥作用的呢?首先呢,Web容器初始化完成后会继续读取web.xml里的节点,我们知道DispatcherServlet是配置在web.xml中的,所以DispatcherServlet是由Web容器主动去加载的。那么,DispatcherServlet到底是个什么呢?

     首先,DispatcherServlet仍是一个Servlet容器。在Web容器初始化的过程中,会创建每个Servlet容器的的实例对象。我们都知道,在Tomcat容器中,维护了一个线程池,每当有一个客户请求抵达服务器的时候,就分配一个线程来处理该请求。同时,Servlet是单例多线程的,也就是多个请求共用一个Servlet容器。所以呢,DispatcherServlet的完整定义是:是一个具有唯一性的增强型Servlet容器。 明确了这一点,我们就可以想象的到,DispatcherServlet具有servlet的大多数特性,包括Servlet的完整生命周期。

   按照惯例,我们需要先找到DispatcherServlet的初始化入口,打开源码,它的继承关系如下:

       

   可以看到,DispatcherServlet拥有一系列的父类,顶级父类是GenericServlet,它是Servlet接口的最简单实现。Web容器会直接调用DispatcherServlet拥有的init()方法,其实现在HttpServletBean类中,如下:

public final void init() throws ServletException {

        // Set bean properties from init parameters.
        PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
        if (!pvs.isEmpty()) {
            try {
                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;
            }
        }
        initServletBean();
    }

   我们需要重点关注initServletBean()方法的调用,它的具体实现在FrameworkServlet类中。
  

  protected final void initServletBean() throws ServletException {
            this.webApplicationContext = initWebApplicationContext();
            initFrameworkServlet();
    }

  protected WebApplicationContext initWebApplicationContext() {

       //ROOT上下文(ContextLoaderListener方式加载的)
        WebApplicationContext rootContext =
                WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;

        if (this.webApplicationContext != null) {
            wac = this.webApplicationContext;

            // 1、在创建该Servlet注入的上下文
            if (wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                if (!cwac.isActive()) {
                    if (cwac.getParent() == null) {
                        cwac.setParent(rootContext);
                    }
                    configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
        if (wac == null) {

            //2、查找已经绑定的上下文
            wac = findWebApplicationContext();
        }
        if (wac == null) {

            //3、如果没有找到相应的上下文,并指定父亲为ContextLoaderListener
            wac = createWebApplicationContext(rootContext);
        }

        if (!this.refreshEventReceived) {

            //4、刷新上下文(执行一些初始化)
            onRefresh(wac);
        }

        if (this.publishContext) {
            String attrName = getServletContextAttributeName();
            getServletContext().setAttribute(attrName, wac);
        }

        return wac;
    }



protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
        if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
            // The application context id is still set to its original default value
            // -> assign a more useful id based on available information
            if (this.contextId != null) {
                wac.setId(this.contextId);
            }
            else {
                // Generate default id...
                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                        ObjectUtils.getDisplayString(getServletContext().getContextPath()) + "/" + getServletName());
            }
        }

        wac.setServletContext(getServletContext());
        wac.setServletConfig(getServletConfig());
        wac.setNamespace(getNamespace());
        wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));


        ConfigurableEnvironment env = wac.getEnvironment();
        if (env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
        }

        postProcessWebApplicationContext(wac);
        applyInitializers(wac);
        wac.refresh();
    }

   initWebApplicationContext()方法不用多说,获取通过ContextLoader初始化完成的Spring容器的上下文,然后以此为父容器来加载DispatcherServlet。如果获取失败,则分情况间接创建一个上下文。追踪代码可以发现最后都是间接调用了configureAndRefreshWebApplicationContext()方法来刷新容器。刷新容器可以类比Spring容器的启动过程,实现过程在AbstractApplicationContext类中。容器刷新工作后就准备完成了,通过onRefresh()方法来做一些初始化工作,它的实现在DispatcherServlet类中,如下:  

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

protected void initStrategies(ApplicationContext context) {    

     initMultipartResolver(context);//文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析  

     initLocaleResolver(context);//本地化解析 

     initThemeResolver(context);//主题解析 

     initHandlerMappings(context);//通过HandlerMapping,将请求映射到处理器 

     initHandlerAdapters(context);//通过HandlerAdapter支持多种类型的处理器 

     initHandlerExceptionResolvers(context);//如果执行过程中遇到异常,将交给HandlerExceptionResolver来解析 

     initRequestToViewNameTranslator(context);//直接解析请求到视图名 

     initViewResolvers(context);//通过viewResolver解析逻辑视图到具体视图实现 

     initFlashMapManager(context);//flash映射管理器 
    }

 以上,就是所有内容了,可以看出这是一种很巧妙的设计。

   

 

  

     

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值