springmvc进阶(2):springmvc初始化

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初始化的过程就结束了

总结

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

猜你喜欢

转载自blog.csdn.net/abc997995674/article/details/80499572