资讯专栏INFORMATION COLUMN

SpringMVC之源码分析--启动过程

Olivia / 2944人阅读

摘要:核心类类的继承关系前端控制器是规范中的核心类,实现接口,继承此类用于处理用户请求。主要配置中初始化参数。

Spring MVC 核心类

类的继承关系
Spring MVC前端控制器DispatcherServlet-->FrameworkServlet-->HttpServletBean-->HttpServlet

HttpServlet

HttpServlet是Servlet规范中的核心类,实现Servlet接口,继承此类用于处理用户请求。

HttpServletBean

HttpServletBean主要配置servlet中初始化参数。继承HttpServlet,并实现无参的init()方法,用于设置在web.xml中配置的contextConfigLocation属性,此属性指定Spring MVC的配置文件地址,默认为WEB-INF/[servlet-name]-servlet.xml,源码如下:

/**
 * DispatcherServlet第一次加载时调用init方法
 */
@Override
public final void init() throws ServletException {
    // 省略日志...
    // 获取在web.xml配置的初始化参数,并将其设置到DispatcherServlet中
    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;
        }
    }
    // 调用子类(FrameworkServlet)进行初始化
    // 模版方法,此方法在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()

FrameworkServlet

FrameworkServlet主要创建WebApplicationContext上下文,重写了HttpServletBean的initServletBean()方法。

1、initServletBean

该方法只有两句关键代码,其作用一个是调用initWebApplicationContext()方法初始化WebApplicationContext上下文,另一个是调用子类方法initFrameworkServlet()方法,源码如下:

@Override
protected final void initServletBean() throws ServletException {
    getServletContext().log("Initializing Spring FrameworkServlet "" + getServletName() + """);
    // 省略日志...
    long startTime = System.currentTimeMillis();
    try {
        // 初始化WebApplicationContext,并调用子类(DispatcherServlet)的onRefresh(wac)方法
        this.webApplicationContext = initWebApplicationContext();
        initFrameworkServlet();
    }
    catch (ServletException | 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");
    }
}
2、initWebApplicationContext
protected WebApplicationContext initWebApplicationContext() {
    // 获取root WebApplicationContext,即web.xml中配置的listener(ContextLoaderListener)
    WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;
    // 判断容器是否由编程式传入(即是否已经存在了容器实例),存在的话直接赋值给wac,给springMVC容器设置父容器
    // 最后调用刷新函数configureAndRefreshWebApplicationContext(wac),作用是把Spring MVC配置文件的配置信息加载到容器中去
    if (this.webApplicationContext != null) {
        // context上下文在构造是注入
        wac = this.webApplicationContext;
        if (wac instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
            if (!cwac.isActive()) {
                // context没有被refreshed,提供一些诸如设置父context、设置应用context id等服务
                if (cwac.getParent() == null) {
                    cwac.setParent(rootContext);
                }
                configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }
    // 在ServletContext中寻找是否有Spring MVC容器,初次运行是没有的,Spring MVC初始化完毕ServletContext就有了Spring MVC容器
    if (wac == null) {
        wac = findWebApplicationContext();
    }
    // 当wac既没有没被编程式注册到容器中的,也没在ServletContext找得到,此时就要新建一个Spring MVC容器
    if (wac == null) {
        // 如果没有WebApplicationContext则创建
        wac = createWebApplicationContext(rootContext);
    }
    // 到这里Spring MVC容器已经创建完毕,接着真正调用DispatcherServlet的初始化方法onRefresh(wac)
    // 此处仍是模板模式的应用
    if (!this.refreshEventReceived) {
        onRefresh(wac);
    }
    // 将Spring MVC容器存放到ServletContext中去,方便下次取出来
    if (this.publishContext) {
        // Publish the context as a servlet context attribute.
        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;
}
3、createWebApplicationContext
protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
    return createWebApplicationContext((ApplicationContext) parent);
}
protected WebApplicationContext createWebApplicationContext(@Nullable 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");
    }
    // 实例化容器
    ConfigurableWebApplicationContext wac =
            (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    // 设置容器环境
    wac.setEnvironment(getEnvironment());
    // 设置父容器
    wac.setParent(parent);
    // 加载Spring MVC的配置信息,如:bean注入、注解、扫描等等
    String configLocation = getContextConfigLocation();
    if (configLocation != null) {
        wac.setConfigLocation(configLocation);
    }
    // 刷新容器,根据Spring MVC配置文件完成初始化操作
    configureAndRefreshWebApplicationContext(wac);
    return wac;
}

总结FrameworkServlet的作用:

创建Spring MVC的容器,根据配置文件实例化里面各种bean,并将之与spring的容器进行关联

把创建出来的Spring MVC容器存放到ServletContext中

通过模板方法模式调用子类DispatcherServlet的onRefresh()方法

DispatcherServlet

DispatcherServlet是Spring MVC核心,它是J2EE规范前端控制器的实现,负责拦截用户请求,并解析请求进行转发。

@Override
protected void onRefresh(ApplicationContext context) {
    initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context); // 文件上传解析
    initLocaleResolver(context); // 本地解析
    initThemeResolver(context); //主题解析
    initHandlerMappings(context); // URL请求映射
    initHandlerAdapters(context); // 初始化Controller类
    initHandlerExceptionResolvers(context); // 异常解析
    initRequestToViewNameTranslator(context); 
    initViewResolvers(context); // 视图解析
    initFlashMapManager(context);
}
总结

容器启动时,加载web.xml部署描述文件,扫描到并找到DispatcherServlet核心控制器

调用HttpServletBean的init()方法,把DispatcherServlet初始化参数设置到DispatcherServlet中,并调用子类FrameworkServlet的initServletBean()方法

FrameworkServlet的initServletBean()创建Spring MVC容器并初始化,并且和Spring父容器进行关联,使得Spring MVC容器能访问Spring容器中定义的bean,之后调用子类DispatcherServlet的onRefresh()方法

DispatcherServlet的onRefresh(ApplicationContext context)对DispatcherServlet的策略组件进行初始化

最后创建了qq群方便大家交流,可扫描加入,同时也可加我qq:276420284,共同学习、共同进步,谢谢!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69261.html

相关文章

  • SpringMVC源码分析--HandlerMapping(一)

    摘要:接口接口作用是将请求映射到处理程序,以及预处理和处理后的拦截器列表,映射是基于一些标准的,其中的细节因不同的实现而不相同。该参数是类型,作用是检查所有的映射解析器或使用或为的,默认为,即从上下文中检查所有的。 概述 在Spring MVC启动章节https://segmentfault.com/a/1190000014674239,介绍到了DispatcherServlet的onRef...

    ralap 评论0 收藏0
  • SpringMVC源码分析--HandlerAdapter(二)

    摘要:概述本章我们主要分析处理组件的处理流程以及其接口源码。概括来说,使用组件分为两步,首先是注册组件,其次是处理用户请求,以下针对这两个过程进行详细的分析。本系列文章是基于。接下来的几章将分析提供的适配策略,希望本节对大家能有帮助,谢谢。 概述 本章我们主要分析Spring处理HandlerAdapter组件的处理流程以及其接口源码。概括来说,Spring使用HandlerAdapter组...

    Eastboat 评论0 收藏0
  • SpringMVC源码分析--HandlerMapping(三)

    摘要:与类图对比,类继承自抽象类,其又继承自抽象类,再往上继承关系与一致。创建初始化上一章我们分析了的创建初始化过程,的创建初始化过程与一样,方法的入口在抽象类中的方法。至此,代码编写完毕。 概述 本节我们继续分析HandlerMapping另一个实现类BeanNameUrlHandlerMapping,从类的名字可知,该类会根据请求的url与spring容器中定义的bean的name属性值...

    fsmStudy 评论0 收藏0
  • SpringMVC源码分析--LocaleResolver和ThemeResolver应用

    摘要:需求根据客户端环境,界面显示不同的国旗图案。选择的技术方案可利用提供的国际化和主题定制来解决。注意此时返回的中没有国际化及主题相关的信息。修改请求参数的值为荷兰,即后再发起请求,结果如下与预期一致,测试通过。 概述 以上分析了Spring MVC的LocaleResolver和ThemeResolver两个策略解析器,在实际项目中很少使用,尤其是ThemeResolver,花精力去分析...

    qpal 评论0 收藏0
  • SpringMVC源码分析--HandlerMapping(二)

    摘要:由于抽象类重写了父类的方法,所以此时会调用的方法,在该方法中通过调用父类的方法,该方法通过模板方法模式最终调到类的方法。分析该类间接实现了接口,直接实现该接口的是抽象类,映射与请求。 概述 在前一章https://segmentfault.com/a/1190000014901736的基础上继续分析,主要完成SimpleUrlHandlerMapping类的原理。 本系列文章是基于Sp...

    Imfan 评论0 收藏0

发表评论

0条评论

Olivia

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<