单独写这一篇我其实蛮忐忑的,毕竟我对于框架源码并不敢说完全“理解”,根本没到那种对原理吃透的程度,很大程度上只能算记录一下我了解到的片面的、表面的Spring MVC的工作机制。
我个人简单的框架认知和idea环境下的项目基本搭建已经另外有记录,分别是 《关于Spring框架以及Spring mvc、boot的基本认知》 和 《IDEA上快速搭建基于maven的Spring MVC项目步骤》这一篇我打算从一些特殊的类和框架对消息进行处理的全流程来记录一下我眼中的spring mvc 。
另外后面又自己参考其他网上的大佬程序员的文章,自己理解并总结了一篇 《从源码理解总结web容器、spring容器、spring mvc容器三者关系》
挖坑,今天太累了,背着起码20斤的背包4个小时地铁、动车、公交轮着乘,而且基本都是站着的,天气有很闷热,实在很疲惫,等自己抽空补充。
——补自己挖的坑,为了记清楚spring mvc框架的大概机制,我打算从【1】简单了解几个容器和它们的关系、【2】容器内重要成员基本介绍、【3】几个容器的启动过程、【4】springMVC的基本工作流程共4个方面逐步递进,以求更详细直白的说清楚,以便自己或他人日后的浏览理解,由于我本人也是学习状态,水平不够,写的时候很多内容是查看了其他老鸟的文章结合自己理解整理的,如有错误也希望别人或者自己发现了能及时纠错。
[之所以没有放全部链接是因为看得文章数量不少,而且较杂,所以只有部分我引用多的会专门贴链接]
第一步,先从宏观来理解Web容器、Servlet容器、Spring容器、SpringMVC容器之间的关系。
1、我个人对容器的概括
容器是个什么概念,我个人从2个方面去理解和解释它:【1】从组成方面讲,容器就是一种将常用功能、操作、配置等统统抽取封装后的集成打包;【2】从功能方面讲:容器就是为核心业务提供通用功能的快捷实现的运行环境。
举个并不很恰当的例子只为了方便理解,就好像从计算码-->C-->高级编程语言比如JAVA等,后者很大程度上是通过对前者的封装调用来达到既简化了编写又实现了功能的结果。而容器就是这样的一种能把需要重复调用的功能都提炼为“环境”并提供这样的舒适环境给其他技术使用的工厂或者说中枢,之所以我喊他工厂或中枢,是因为容器还拥有生产、控制调度、销毁的能力,但容器说到底是为核心功能服务的。
【以下容器简介内容我主要摘录了《Web容器、Servlet容器、Spring容器、SpringMVC容器之间的关系》,感谢所有分享了心得的大佬】
2、web容器
web容器(web服务器)主要有:Apache、IIS、Tomcat、Jetty、JBoss、webLogic等,而Tomcat、Jetty、JBoss、webLogic同时也是servlet容器,或者说他们还包含了servlet容器。没有servlet容器,你也可以用web容器直接访问静态页面,比如安装一个apache等,但是如果要显示jsp/servlet,你就要安装一个servlet容器了,但是光有servlet容器是不够的,因为它要被解析成html输出,所以你仍需要一个web容器。大多数servlet容器同时提供了web容器的功能,也就是说大多servelt容器可以独立运行你的web应用。
web容器是管理servlet(通过servlet容器),以及监听器(Listener)和过滤器(Filter)的。这些都是在web容器的掌控范围里。但他们不在spring和springmvc的掌控范围里。因此,我们无法在这些类中直接使用Spring注解的方式来注入我们需要的对象,是无效的,web容器是无法识别的。
这里记录一下web项目中web.xml文件的简单知识点,引用自 《web.xml文件是什么?有什么用?--详解》:
经过个人测试,WEB工程加载顺序与元素节点在文件中的配置顺序无关。即不会因为 filter 写在 listener 的前面而会先加载 filter。WEB容器的加载顺序是:ServletContext -> context-param -> listener -> filter -> servlet。并且这些元素可以配置在文件中的任意位置。
加载过程顺序如下:
启动一个WEB项目的时候,WEB容器会去读取它的配置文件web.xml,读取<listener>和<context-param>两个结点。
紧急着,容创建一个ServletContext(servlet上下文),这个web项目的所有部分都将共享这个上下文。
容器将<context-param>转换为键值对,并交给servletContext。
容器创建<listener>中的类实例,创建监听器。
3、Servlet容器
Servlet容器是管理servlet对象的。Servlet容器的作用:我们都知道servlet能相应所有请求,所以servlet容器能负责处理客户请求,当客户请求来到时,Servlet容器获取请求,然后调用某个Servlet,并把Servlet的执行结果返回给客户。
使用Servlet容器的原因:
通信支持:利用容器提供的方法,你能轻松的让servlet与web服务器对话,而不用自己建立serversocket、监听某个端口、创建流等 等。容器知道自己与web服务器之间的协议,所以你的servlet不用担心web服务器(如Apache)和你自己的web代码之间的API,只需要考虑如何在servlet中实现业务逻辑(如处理一个订单)。
生命周期管理:servlet容器控制着servlet的生与死,它负责加载类、实例化和初始化servlet,调用servlet方法,以及使servlet实例被垃圾回收,有了servlet容器,你不需要太多的考虑资源管理。
多线程支持:容器会自动为它所接收的每个servlet请求创建一个新的java线程。针对用户的请求,如果servlet已经运行完相应的http服务方法,这个线程就会结束。这并不是说你不需要考虑线程安全性,其实你还会遇到同步问题,不过这样能使你少做很多工作。
声明方式实现安全:利用servlet容器,可以使用xml部署描述文件来配置和修改安全性,而不必将其硬编码写到servlet类代码中。
JSP支持:servlet容器负责将jsp代码翻译为真正的java代码。
4、Spring容器
Spring容器是管理service和dao的,在spring mvc项目中属于mvc容器的父容器,但无法访问子容器中的bean。
5、SpringMVC容器
SpringMVC容器是管理controller对象的,是spring容器的子容器。
Spring容器和SpringMVC容器的关系是父子容器的关系。Spring容器是父容器,SpringMVC容器是子容器。在子容器里可以访问父容器里的对象,但是在父容器里不可以访问子容器的对象,说的通俗点就是,在controller里可以访问service对象,但是在service里不可以访问controller对象。所以这么看的话,所有的bean,都是被Spring或者SpringMVC容器管理的,他们可以直接注入。然后SpringMVC的拦截器也是SpringMVC容器管理的,所以在SpringMVC的拦截器里,可以直接注入bean对象。
第二步,容器内几个重要成员的介绍
[由于对于框架源码的了解不够深刻,我参考了大佬的文章《通过tomcat容器启动spring容器的启动过程》部分内容。]
几个容器顺序初始化的过程中起作用的成员肯定不止这几个,以下只介绍几个容器直接相关的,另外还有部分成员会放在第三步启动过程以及第四步mvc工作流程中提到的时候顺便说明。
1、ServletContext
ServletContext是servlet与servlet容器之间的直接通信的接口。Servlet容器在启动一个Web应用时,会为它创建一个servletContext对象。每个web应用有唯一的servletContext对象。同一个web应用的所有servlet对象共享一个serveltContext,servlet对象可以通过它来访问容器中的各种资源。
2、contextLoaderListener
ContextLoaderListener继承自ContextLoader,实现的是ServletContextListener接口,在启动Web容器时,读取在contextConfigLocation中定义的xml文件,自动装配ApplicationContext的配置信息,并产生WebApplicationContext对象,然后将这个对象放置在ServletContext的属性里,这样我们只要得到Servlet就可以得到WebApplicationContext对象,并利用这个对象访问spring容器管理的bean。《Spring中ContextLoaderListener作用》
简单来说,就是上面这段配置为项目提供了spring支持,初始化了Ioc容器
3、WebApplicationContext
我个人理解这就是spring IOC容器,WebApplicationContext是专门为web应用准备的,他允许从相对于web根目录的路劲中装载配置文件完成初始化工作,从WebApplicationContext中可以获得ServletContext的引用,整个Web应用上下文对象将作为属性放置在ServletContext中,以便web应用可以访问spring上下文,spring中提供WebApplicationContextUtils的getWebApplicationContext(ServletContext src)方法来获得WebApplicationContext对象。《Spring-MVC理解之一:应用上下文webApplicationContext》
4、DispatcherServlet
这是前端控制器,或者说请求分发servlet,它本质上其实就是个servlet,关键是它响应请求的过程中承担了特殊的功能,具体会在第四步中详细记录,SpringMVC的核心就是DispatcherServlet。《DispatcherServlet作用》[这篇写的超详细,我的图也从这位大佬这里拿了一张]
第三步,容器启动过程(Spring MVC项目)
恩,其实看完上面的内容,尤其是对引用的链接文章仔细查阅后,基本上心中已经有了七七八八的理解,那再归纳下容器启动过程。
1、servlet容器启动,为应用创建一个“全局上下文环境”:ServletContext ;
2、容器调用web.xml中配置的contextLoaderListener,初始化WebApplicationContext上下文环境(即IOC容器),加载context-param指定的配置文件信息到IOC容器中。WebApplicationContext在ServletContext中以键值对的形式保存;
3、容器初始化web.xml中配置的servlet,为其初始化自己的上下文信息servletContext,并加载其设置的配置信息到该上下文中。将WebApplicationContext设置为它的父容器。 如果配置了spring mvc的话,DispatcherServlet也就在这一步初始化,DispatcherServlet上下文在初始化的时候会建立自己的IoC上下文,用以持有spring mvc相关的bean,也就是因为这一步,所以我们说spring mvc是spring的子容器。
4、此后的所有servlet的初始化都按照3步中方式创建,初始化自己的上下文环境,将WebApplicationContext设置为自己的父上下文环境。
第四步,springMVC的基本工作流程
走到第四步,也就是最后一步,其实截至spring mvc初始化的基本内容已经记录得七七八八了,下面要讲的,是spring mvc本身的工作流程。
[这里有一篇大佬的《pringMVC详细流程(一)》写的也很清楚]
流程说明:
第一步:发起请求到前端控制器(DispatcherServlet)
第二步:前端控制器请求HandlerMapping查找 Handler (可以根据xml配置、注解进行查找)
第三步:处理器映射器HandlerMapping向前端控制器返回Handler,HandlerMapping会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象,多个HandlerInterceptor拦截器对象),通过这种策略模式,很容易添加新的映射策略
第四步:前端控制器调用处理器适配器去执行Handler
第五步:处理器适配器HandlerAdapter将会根据适配的结果去执行Handler
第六步:Handler执行完成给适配器返回ModelAndView
第七步:处理器适配器向前端控制器返回ModelAndView (ModelAndView是springmvc框架的一个底层对象,包括 Model和view)
第八步:前端控制器请求视图解析器去进行视图解析 (根据逻辑视图名解析成真正的视图(jsp)),通过这种策略很容易更换其他视图技术,只需要更改视图解析器即可
第九步:视图解析器向前端控制器返回View
第十步:前端控制器进行视图渲染 (视图渲染将模型数据(在ModelAndView对象中)填充到request域)
第十一步:前端控制器向用户响应结果
部分组件介绍:
(1) 前端控制器DispatcherServlet。
作用:接收请求,响应结果,相当于转发器,中央处理器。有了DispatcherServlet减少了其它组件之间的耦合度。
(2) 处理器映射器HandlerMapping。
作用:根据请求的url查找Handler。
(3) 处理器适配器HandlerAdapter。
作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler。
(4) 处理器Handler。
注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler
(5) 视图解析器ViewResolver。
作用:进行视图解析,根据逻辑视图名解析成真正的视图(view)
(6) 视图View。
注意:View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf…)
(呼,差不多写完了,为了写这一篇可以说花了很多时间上网查看很多大佬的文章,同一个知识点不同文章之间对比、知识点和自己配置项目的配置文件内容比照、部分源代码和贴图对比.....实际上这篇文章是好几天拼凑起来的,每天写一点,有时候写了又删了来来回回。为了尽量避免错误,期间反反复复修改,虽然最终的完成稿内包含了很大一部分大佬文章的摘抄,但也是我仔细查阅、理解和整理筛选的,可以说花了很多心血,也在这个过程中又深刻了自己的见解,希望自己加油,更多的写这样内容多的文章,边学边记边复习,加油!)
[对了,还要安利下慕课网,我写这篇文章也在免费课程《Spring MVC起步》取了经。这个程序员学习平台很不错,主要体现在上面大佬多,学习的氛围也浓,很多课程虽然许要花一定的钱购买,但如果真心要学这点小钱一定不亏,最可贵的是很多大牛在上面分享了自己的免费课程,只能说:感谢这样的技术牛人愿意无偿提供分享!]
[最近有偶尔看到一篇别人写的文章 《Spring MVC实现原理解析》看了感觉人家写的确实比我好,很明显就是理解的更深刻,而且人家记录文章的层次和结构也更清晰,厉害厉害]