SpringMVC初始化过程

ps:以下所有源码都是spring4.2.4版本
springmvc是主流的MVC框架,我先从使用开始一步一步解析其初始化过程,springmvc的核心是DispatcherServlet,它是前端控制器,负责拦截客户端发过来的请求,然后解析请求进行分发。
DispatcherServlet是基于Servlet的,所以使用springmvc先在web.xml中配置DispatcherServlet

<!-- 配置DisaptcherServlet -->
    <servlet>
        <servlet-name>springMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 初始化参数,配置springmvc配置文件 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springMVC.xml</param-value>
        </init-param>
        <!-- web容器启动时加载该Servlet -->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>springMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

配置文件解析:可以看到在web.xml配置了一个名叫DispatcherServlet的Servlet,所有的请求都会在它那里执行,并且它有一个名叫contextConfigLocation的参数,在这里赋值为classpath:springMVC.xml(即mvc的配置文件位置所在)
接下来看DispatcherServlet主要的继承关系
这里写图片描述
图中每个类中的方法是待会分析初始化过程会调用的主要方法,可以看到DispatcherServlet本质还是一个Servlet,上面说到在web容器启动时会会加载DispatcherServlet,每个Servlet在第一次加载时都会调用其init()方法,但是DispatcherServlet本身没有这个方法,所以系统会去它父类寻找init()方法,最后在HttpServletBean找到,调用,以下是init()方法源码

1.HttpServletBean的init()方法
//DispatcherServlet第一次加载时调用init方法
@Override
    public final void init() throws ServletException {
        if (logger.isDebugEnabled()) {
            logger.debug("Initializing servlet '" + getServletName() + "'");
        }

        // Set bean properties from init parameters.
        /**
         *概括来说:
         *try语句块的作用就是获取刚刚在web.xml配置的初始化参数<init-param>
         *并将这些参数设置到DispatcherServlet中
         */
        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) {
            logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
            throw ex;
        }

        // Let subclasses do whatever initialization they like.
        //模版方法,此方法在HttpServletBean本身是空的,但是因为调用方法的对象是DispatcherServlet
        //所以优先在DispatcherServlet找,找不到再去父类找,最后在FrameworkServlet找到
        initServletBean();

        if (logger.isDebugEnabled()) {
            logger.debug("Servlet '" + getServletName() + "' configured successfully");
        }
    }

总结HttpServletBean的作用:

  • 获取web.xml的中配置DispatcherServlet的初始化参数,存放到一个参数容器ServletConfigPropertyValues中
  • 根据传进来的this创建对象包 裹者(BeanWrapper),本质上它就是DispatcherServlet
  • 最后通过bw.setPropertyValues(pvs, true);把参数设置到bw(即DispatcherServlet)里面去,最后调用子类的initServletBean()
2.FrameworkServlet的initServletBean()方法源码
@Override
    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 {
            //重要代码,创建springmvc的ioc容器实例
            this.webApplicationContext = initWebApplicationContext();
            initFrameworkServlet();
        }
        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");
        }
    }

看到代码中通过initWebApplicationContext()就返springMVC的容器实例了,接下来看initWebApplicationContext()源码

protected WebApplicationContext initWebApplicationContext() {
        //首先通过ServletContext获得spring容器,因为子容器springMVC要和父容器spring容器进行关联
        WebApplicationContext rootContext =
                WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        //定义springMVC容器wac
        WebApplicationContext wac = null;

        //判断容器是否由编程式传入(即是否已经存在了容器实例),存在的话直接赋值给wac,给springMVC容器设置父容器
        //最后调用刷新函数configureAndRefreshWebApplicationContext(wac),作用是把springMVC.xml的配置信息加载到容器中去
        if (this.webApplicationContext != null) {
            // A context instance was injected at construction time -> use it
            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                if (!cwac.isActive()) {

                    if (cwac.getParent() == null) {
                        // The context instance was injected without an explicit parent -> set
                        // the root application context (if any; may be null) as the parent
                        cwac.setParent(rootContext);
                    }
                    configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
        if (wac == null) {
            // 在ServletContext中寻找是否有springMVC容器,初次运行是没有的,springMVC初始化完毕ServletContext就有了springMVC容器
            //具体原因看下面46行代码
            wac = findWebApplicationContext();
        }

        //当wac既没有没被编程式注册到容器中的,也没在ServletContext找得到,此时就要新建一个springMVC容器
        if (wac == null) {
            // 创建springMVC容器
            wac = createWebApplicationContext(rootContext);
        }

        if (!this.refreshEventReceived) {
            //到这里mvc的容器已经创建完毕,接着才是真正调用DispatcherServlet的初始化方法onRefresh(wac)
            onRefresh(wac);
        }

        if (this.publishContext) {
            //将springMVC容器存放到ServletContext中去,方便下次取出来
            String attrName = getServletContextAttributeName();
            getServletContext().setAttribute(attrName, wac);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                        "' as ServletContext attribute with name [" + attrName + "]");
            }
        }

        return wac;
    }

接下来看创建springMVC 的ioc容器方法createWebApplicationContext(WebApplicationContext parent)

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
        Class<?> contextClass = getContextClass();
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Servlet with name '" + getServletName() +
                    "' will try to create custom WebApplicationContext context of class '" +
                    contextClass.getName() + "'" + ", using parent context [" + parent + "]");
        }
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException(
                    "Fatal initialization error in servlet with name '" + getServletName() +
                    "': custom WebApplicationContext class [" + contextClass.getName() +
                    "] is not of type ConfigurableWebApplicationContext");
        }
        //实例化空白的ioc容器
        ConfigurableWebApplicationContext wac =
                (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
        //给容器设置环境
        wac.setEnvironment(getEnvironment());
        //给容器设置父容器(就是spring容器),两个ioc容器关联在一起了
        wac.setParent(parent);
        //给容器加载springMVC.xml的配置信息,bean注入,注解,扫描等等
        wac.setConfigLocation(getContextConfigLocation());
        //上面提到过这方法,刷新容器,根据springMVC.xml配置文件完成初始化操作,此时springMVC容器创建完成
        configureAndRefreshWebApplicationContext(wac);

        return wac;
    }

以上就是FrameworkServlet创建容器的过程
总结FrameworkServlet作用:

  • 创建springMVC的ioc容器根据配置文件实例化里面各种bean,并将之与spring的ioc容器进行关联
  • 把创建出来的mvc容器存放到ServletContext中

接下来看DispatcherServlet的onRefresh(ApplicationContext context)方法

3.DispatcherServlet的onRefresh(ApplicationContext context)方法
@Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

很简单,调用了initStrategies(context),初始化策略组件

protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);//文件上传解析
        initLocaleResolver(context);//本地解析
        initThemeResolver(context);//主题解析
        initHandlerMappings(context);//url请求映射
        initHandlerAdapters(context);//初始化真正调用controloler方法的类
        initHandlerExceptionResolvers(context);//异常解析
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);//视图解析
        initFlashMapManager(context);
    }

至此,整个springMVC初始化的过程就结束了

总结

  1. web应用启动时扫描web.xml文件,扫描到DispatcherServlet,对其进行初始化
  2. 调用DispatcherServlet父类的父类HttpServletBean的init()方法,把配置DispatcherServlet的初始化参数设置到DispatcherServlet中,调用子类FrameworkServlet的initServletBean()方法
  3. initServletBean()创建springMVC容器实例并初始化容器,并且和spring父容器进行关联,使得mvc容器能访问spring容器里面的bean,之后调用子类DispatcherServlet的onRefresh(ApplicationContext context)方法
  4. onRefresh(ApplicationContext context)进行DispatcherServlet的策略组件初始化工作,url映射初始化,文件解析初始化,运行适配器初始化等等。

到此文章已经写完了,如果出现错误,请指出,十分感谢你的指教。

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值