框架源码是我们 Coding 晋级中的必修课,SSM 应该算是小伙伴们日常接触最多的框架了,这其中 SpringMVC 初始化流程相对来说要简单一些,因此今天松哥就先来和大家分析一下 SpringMVC 初始化流程。,本文算是 SpringMVC 用法的一个进阶,如果小伙伴们对 SpringMVC 的基础用法还不熟悉,可以在公众号后台回复 ssm,有松哥录制的免费视频教程。,即使你没看过 SpringMVC 的源码,估计也听说过:DispatcherServlet 是 SpringMVC 的大脑,它负责整个 SpringMVC 的调度工作,是 SpringMVC 中最最核心的类,SpringMVC 整个顶层架构设计都体现在这里,所以搞明白 DispatcherServlet 的源码,基本上 SpringMVC 的工作原理也就了然于胸了。,然而 DispatcherServlet 继承自 FrameworkServlet,FrameworkServlet 又继承自 HttpServletBean,如下图:,
,因此我们的分析就从 HttpServletBean 开始。,HttpServletBean 继承自 HttpServlet,它负责将 init-param 中的参数注入到当前 Servlet 实例的属性中,同时也为子类提供了增加 requiredProperties 的能力,需要注意的是 HttpServletBean 并不依赖于 Spring 容器。,大家知道,HttpServlet 的初始化是从 init 方法开始的,所以我们就先从 HttpServletBean 的 init 方法开始看起:,在这个方法里,首先获取到 Servlet 的所有配置并转为 PropertyValues,然后通过 BeanWrapper 修改目标 Servlet 的相关属性。BeanWrapper 是 Spring 中提供一个工具,使用它可以修改一个对象的属性,像下面这样:,最终输出:,所以前面的 bw 实际上就代表当前 DispatcherServlet 对象。,通过 BeanWrapper 修改目标 Servlet 的相关属性时,有一个 initBeanWrapper 方法是空方法,开发者如有需要可以在子类中实现该方法,并且完成一些初始化操作。,属性配置完成后,最终调用 initServletBean 方法进行 Servlet 初始化,然而该方法也是一个空方法,在子类中实现。,这就是 HttpServletBean 所做的事情,比较简单,加载 Servlet 相关属性并设置给当前 Servlet 对象,然后调用 initServletBean 方法继续完成 Servlet 的初始化操作。,从前面的介绍可知,FrameworkServlet 初始化的入口方法就是 initServletBean,因此我们就从 FrameworkServlet#initServletBean 方法开始看起:,这个方法原本挺长的,但是抛开日志打印异常抛出,剩下的核心代码其实就两行:,那么这里最为重要的其实就是 initWebApplicationContext 方法了,我们一起来看下:,这里的逻辑也比较清晰:,上面的这些步骤中,通过 createWebApplicationContext 方法创建 WebApplicationContext 对象需要和大家细说下,因为一般情况下就是通过这种方式创建的 WebApplicationContext。我们来看一下相关的方法:,这里一共涉及到两个方法:,首先获取到创建类型,并检查创建类型,没问题的话调用 instantiateClass 方法完成创建工作,然后给创建好的 wac 对象配置各种属性,配置的 configLocation 就是我们在 web.xml 文件中配置的 SpringMVC 配置文件路径,默认的文件路径是 /WEB-INF/[servletName]-servlet.xml。,configureAndRefreshWebApplicationContext 方法主要也是配置&刷新 WebApplicationContext,在这个方法里会调用 addApplicationListener 为 wac 添加一个监听器,监听的是 ContextRefreshedEvent 事件,当收到该事件后,会调用 FrameworkServlet 的 onApplicationEvent 方法,并在该方法中调用 onRefresh 方法完成刷新,刷新之后,会将 refreshEventReceived 变量标记为 true。,这就是 FrameworkServlet#initServletBean 方法的大致工作逻辑。这里涉及到了 onRefresh 方法,但是这是一个空方法,在子类 DispatcherServlet 中实现了,所以接下来我们就来看 DispatcherServlet。,这里我们就不废话了,直接来看 onRefresh 方法,如下:,在 onRefresh 方法中调用了 initStrategies 进行初始化操作。initStrategies 的内容其实很简单,就是九个组件的初始化。九个的初始化流程比较类似,这里我们以常见的视图解析器的初始化方法 initViewResolvers 为例,来一起看看初始化流程:,一开始的 viewResolvers 变量是一个集合,解析出来的视图解析器对象都将放入这个集合中。,首先判断 detectAllViewResolvers 变量是否为 true,如果为 true,则直接去查找 Spring 容器中的所有视图解析器,将查找结果赋值给 viewResolvers,然后进行排序。默认情况下 detectAllViewResolvers 变量的值为 true,如果有需要,可以在 web.xml 中进行配置,像下面这样:,如果 detectAllViewResolvers 的值为 false,那么接下来就会去 Spring 容器中查找一个名为 viewResolver 的视图解析器,此时查找到的就是一个单独的视图解析器。,一般来说,我们并不需要在 web.xml 中去配置 detectAllViewResolvers 的值,视图解析器有多少个就加载多少个。,举个简单例子,我们在 SpringMVC 的配置文件中可能像下面这样配置视图解析器:,默认情况下,这个 bean 的 id 有没有都行,如果有,取什么值都可以,反正最终都是通过类型而不是 id 去查找的视图解析器。但是如果你在 web.xml 中将 detectAllViewResolvers 修改为 false,那么这个 bean 的 id 取值就比较重要了,就一定要是 viewResolver。,如果在 Spring 容器中通过这两种方式(通过类型查找或通过 id 查找)都没有找到 ViewResolver 实例,那么会调用 getDefaultStrategies 方法去获取一个默认的 ViewResolver 实例。默认实例的获取方式如下:,这段代码其实也比较简单,就是通过反射去获取默认的视图解析器。,首先给 defaultStrategies 赋值,defaultStrategies 的值实际上就是从 DispatcherServlet.properties 文件中加载到的,我们来看下这个文件内容:,
,可以看到,这里一共定义了 8 个默认的键值对,有的值是一个,有的值是多个。前面 initStrategies 方法中一共要初始化九个组件,这里默认只定义了 8 个,少了一个 MultipartResolver,这也好理解,并非所有的项目都有文件上传,而且即使有文件上传,用哪一个具体的 MultipartResolver 也不好确定,还是要开发者自己决定。,defaultStrategies 其实加载到的就是这 8 个键值对,其中视图解析器对应的是 org.springframework.web.servlet.view.InternalResourceViewResolver,通过反射创建该类的实例,当 Spring 容器中不存在任何视图解析器的时候,默认的视图解析器即此。,这就是 initViewResolvers 的工作流程,另外 8 个也和它差不多,唯一不同的是 initMultipartResolver,如下:,可以看到,它只是根据 bean 的名字去查找 bean 实例,没有去查找默认的 MultipartResolver。,说到这里,松哥和大家多说一句 SpringMVC 配置中的小细节,,上面这个关于视图解析器和文件上传解析器的配置,不知道小伙伴们有没有注意过,视图解析器的 id 可有可无,而文件上传解析器的 id 必须是 multipartResolver,回顾我们上面的源码分析,你就知道为啥了!,好啦,这就是松哥和小伙伴们分享的 SpringMVC 的初始化流程,主要涉及到了 HttpServletBean、FrameworkServlet 以及 DispatcherServlet 三个实例,HttpServletBean 主要是加载 Servlet 配置的各种属性并设置到 Servlet 上;FrameworkServlet 则主要是初始化了 WebApplicationContext;DispatcherServlet 则主要是初始化了自身的九个组件。,这只是初始化的流程,那么当请求到来之后,请求的流程又是怎么样的呢?
© 版权声明
文章版权归作者所有,未经允许请勿转载。