在日常调试项目时,总是利用tomcat去启动项目,并进行前后端联调,但对于前后端的请求响应的交互原理及过程并不是特别清晰。 为什么在前端发出相应请求,就能跳转到后端通过程序得到结果再响应到前端页面呢?! 为了加深对该过程的理解,故以tomcat为例,撰写此文。
在日常调试项目时,总是利用tomcat去启动项目,并进行前后端联调,但对于前后端的请求响应的交互原理及过程并不是特别清晰。
为什么在前端发出相应请求,就能跳转到后端通过程序得到结果再响应到前端页面呢?!
为了加深对该过程的理解,故以tomcat为例,撰写此文。
一、Tomcat部分
Tomcat总体结构:
Server->Service->Connector&Container(Engine->Host->Context(Wrapper(Servlet)))
如图所示的Ttomcat结构图,其核心是Connector和Container组件,其中Connector组件可以被替换从而提供更多选择,因此一个Container可以选择对应多个Connector。
Server
多个Connector和一个Container就形成了一个Service,就有了对外提供服务的能力。而光有Service还不行,还必须给其一个生存环境去发挥去作用,这项生杀大权的掌握者就花落Server手中了。
因此,Tomcat的生命周期就由Server来控制。
Server的作用很简单,就是对外提供接口让其他程序能够访问到其中的Service集合,同时维护包含的所有Service的生命周期,如初始化、找到对应service和结束服务等。
Service
对于Service来说,主要是对外提供服务,其中的Connector主要是负责对外交流,而Container则是处理内部事务。一个Service可有多个Connector,但只能有一个Container。
Connector
对于Connector来说,它将在某个指定的端口上来监听客户请求,把从socket传递过来的数据封装成Request,传递给Engine来进行处理,并从Engine处获得响应并返回给客户。Tomcat通常会用到两种Connector:Http Connector,在端口8080处监听来自客户browser的http请求;AJP Connector,在端口8009处监听来自其他webServer的Servlet/jsp代理请求。
Container
在Container核心组件中,包含了以下几个核心部分:Wrapper、Host、Engine、Context。
其中Engine是负责处理所有相关联的service请求,并将结果返回给service。而Connector则是作为service和engine之间的桥梁。一个engine下可以配置一个默认主机,每个虚拟主机都有一个域名。当engine接收到一个请求时,它会将该请求匹配到虚拟主机(host)上,然后将请求交给host来处理。若无法匹配到虚拟主机,则将其交给默认host来处理,以线程方式来启动host。
Host代表一个虚拟主机,每个虚拟主机和某网络域名相匹配。每个虚拟主机下可以有一个或多个web应用,每个web应用对应于一个context,相对应有contextpath。当主机接收到请求时,会将该请求匹配到某个context上,然后把请求交给该context来处理。
一个Context对应于一个web应用。一个web应用由一个或多个servlet组成。context在创建时将根据配置文件$ CATALINA_HOME/conf/web.xml和$ WEBAPP_HOME/WEB-INF/web.xml载入Servlet类。当Context获得请求时,将在自己的映射表(mapping table)中寻找相匹配的Servlet类,如果找到,则执行该类,获得请求的回应,并返回。
Wrapper代表了一个Servlet,负责管理Servlet的装载、初始化、执行以及资源回收。wrapper的实现类是StandardWrapper,该类还实现了拥有一个Servlet初始化信息的ServletConfig。
Lifecycle:在编程中也有很多对象是具有生命周期的,从初始化、运行、回收等 会经历几个不同的阶段。 在tomcat中容器相关的好多组建都实现了Lifecycle接口,当tomcat启动时,其依赖的下层组件会全部进行初始化。 并且可以对每个组件生命周期中的事件添加监听器。例如当服务器启动的时候,tomcat需要去调用servlet的init方法和初始化容器等一系列操作,而停止的时候,也需要调用servlet的destory方法。而这些都是通过org.apache.catalina.Lifecycle接口来实现的。由这个类来制定各个组件生命周期的规范。
Tomcat-Servlet的过程:
1、Tomcat在启动时,会加载server;
2、Server启动时,会加载Service;
3、Service中会加载Container;
4、Container中的Wrapper含有Servlet;
5、在HttpServlet中含有一些doGet、doPost、service等方法,用来处理各种类型的请求。
Tomcat Server处理一个http请求的过程:
假设来自客户的请求为:
http://localhost:8080/test/index.jsp
1) 请求被发送到本机端口8080,被在那里侦听的Coyote HTTP/1.1 Connector获得
2) Connector把该请求交给它所在的Service的Engine来处理,并等待来自Engine的回应
3) Engine获得请求localhost/test/index.jsp,匹配它所拥有的所有虚拟主机Host
4) Engine匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机)
5) localhost Host获得请求/test/index.jsp,匹配它所拥有的所有Context
6) Host匹配到路径为/test的Context(如果匹配不到就把该请求交给路径名为""的Context去处理)
7) path="/test"的Context获得请求/index.jsp,在它的mapping table中寻找对应的servlet
8) Context匹配到URL PATTERN为*.jsp的servlet,对应于JspServlet类
9) 构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet或doPost方法
10)Context把执行完了之后的HttpServletResponse对象返回给Host
11)Host把HttpServletResponse对象返回给Engine
12)Engine把HttpServletResponse对象返回给Connector
13)Connector把HttpServletResponse对象返回给客户browser
二、Serlvet原理
从Tomcat部分我们可以看出,browser的请求在层层传递下最终在Servlet处进行请求的处理及响应。因此本部分主要围绕传递过来的请求和响应部分来进行阐述。
1、认知Servlet
作为Javaweb三大组件(Servlet,Filter,Listener)之一的Servlet,每一个都是唯一的,所能处理的请求是不同的。
Servlet是一种独立于平台和协议的服务器端的java应用程序,运行于java服务器中,可以动态扩展服务器能力,并采用请求-响应模式来提供web服务。
Servlet是一个单实例多线程的。只能被实例化一次,而每次service服务会开启新线程进行处理新请求。
2、Servlet生命周期
前面说到,在Tomcat核心组件Container中,一个web应用由一个或多个Servlet组成,而一个Context对应一个web应用,在创建时会去根据配置文件$ CATALINA_HOME/conf/web.xml和$ WEBAPP_HOME/WEB-INF/web.xml载入Servlet类。
因此Servlet的生命周期由web容器来负责,根据web.xml来加载对应的Servlet类。Servlet核心代码如下:
在上图所示的五个方法中,其中init、service和destroy三个方法均为生命周期方法,在第一次被访问时出生,在关闭服务器时死亡。
init方法会在Servlet对象创建之后马上执行,且只执行一次。
destroy方法会在Servlet被销毁之前调用,也只执行一次。
对于service方法,则可以被多次调用,每次处理请求时都是在调用该方法。
getServletConfig可以获得Servlet的配置信息。
getServletInfo方法可以获得Servlet信息。
从Servlet.class文件中我们可以看到,与之相关联的由ServletConfig、ServletRequest、ServletResponse三个类,这三个类都是通过web容器传递给Servlet的。
其中,ServletConfig是在Servlet初始化时就通过web.xml文件解析传给了Servlet,后两个都是请求到达时调用Servlet.service时才传递过来的。
在第一部分讲解Wrapper时也说过,它代表了一个Servlet。通过查询源码发现,StandardWrapper和StandardWrapperFacede都实现了ServletConfig接口,而 StandardWrapperFacade 是 StandardWrapper 门面类(门面设计模式)。所以传给 Servlet 的是 StandardWrapperFacade 对象,这个类能够保证从 StandardWrapper 中拿到 ServletConfig 所规定的数据,而又不把 ServletConfig 不关心的数据暴露给 Servlet。
3、Servlet工作过程
1)browser发出一个http请求;
2)Tomcat的Connector组件监听到该请求,其主线程会创建HttpServletRequest对象和HTTPServletResponse对象;
3)从请求URL中找到正确Servlet后,Tomcat为其创建或分配一个线程,同时将2中对象传递给该线程;
4)Tomcat调用Servlet的service()方法,会根据请求参数的不同来调用doGet()或doPost()等方法,并将结果返回到HTTPServletResponse对象中;
5)Tomcat将响应结果返回到browser。
三、HTTP与服务器的交互方式
1、http简介
HTTP协议,即超文本传输协议,基于TCP/IP通信协议来传输数据,工作于客户端-服务端架构上,通过URL向Web服务器(Apache服务器等)传输请求并得到响应。默认端口为80,也可以设置为8080等。
HTTP三点注意事项:
HTTP是无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
HTTP是媒体独立的:这意味着,只要客户端和服务器知道如何处理的数据内容,任何类型的数据都可以通过HTTP发送。客户端以及服务器指定使用适合的MIME-type内容类型。
HTTP是无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
2、http消息结构
客户端发送一个HTTP请求到服务器的请求消息包括以下格式:请求行(request line)、请求头部(header)、空行和请求数据四个部分组成,
下图给出了请求报文的一般格式。
HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。
3、请求方式
HTTP1.0有三种:get、post、head;
HTTP1.1新增了五种:options、put、delete、trace和connect。
其中较为常用的为get、post、delete和put,这大致对应着对该资源的查、改、删、增四个操作。
1)Get请求用于向服务器进行信息获取,是安全和幂等的。它仅仅是为了获取信息,不会影响资源的状态;所谓幂等,即对于同一个URL的多个请求返回的结果都一致。
get请求会将数据附在URL之后,以?来进行分割,参数之间以&来进行连接。对于非英文字母/数字等,都需要进行格式的转换。而由于其在URL进行拼接,对于涉及到密码等请求,是不安全的。
在HTTP协议中对URL长度并没有作出限制,而URL的最大长度其实和用户浏览器以及web服务器有关。如IE为2048,Google为8182,Apache(Server)为8192。
2)Post请求表示向服务器提交数据的一种请求,可能修改服务器上的资源,类似数据库的insert一样。post对于数据的提交是放置在http包的包体当中的。理论上post请求是没有大小限制的,起限制作用的是服务器处理程序的处理能力。如IIS 6.0默认post数据最大为200KB,每个表单限制为100KB。post 的安全性比get高。
3)Put请求也是向服务端发送数据从而改变信息,类似于数据库的update一般。
4)Delete请求就是删除某一资源的,类似于数据库的delete操作。
四、SpringMVC下前后端交互过程
1、交互过程
由于目前接触的项目是建立在springMVC模式下的,故根据上图所示,在最后对SpringMVC下的请求响应过程进行解析:
1)用户发送一个URL请求到前端控制器DispatcherServlet;
2)前端控制器将请求发给处理器映射器HandlerMapping,它会根据xml配置、注解等进行查找hander;
3)处理器映射器返回执行链HandlerExecutionChain(里面有handler对象,在它之前有多个interceptor拦截器);
4)前端控制器通过处理器适配器HandlerAdapter去执行Handler,不同的Handler由不同的适配器执行;
5)通过Handler处理器,即我们熟悉的Controller来处理业务逻辑;
6)处理完之后,返回ModelAndView对象,其中有视图名称,模型数据等;
7)HandlerAdapter将ModelAndView返回给DispatcherServlet;
8)DispatcherServlet将得到的ModelAndView传递给视图解析器ViewResolver进行解析;
9)ViewResolver解析后返回具体的视图View;
10)前端控制器对视图View和数据进行渲染,将模型数据等填充到request域中;
11)将最终的视图返回给客户,产生response响应。
2、组件名词解释
前端控制器DispatcherServlet:接收请求响应结果,相当于转发器、中央处理器,减少了其他组件之间的耦合度;
处理器映射器HandlerMapping:根据请求URL查找handler;
处理器适配器HandlerAdapter:按特定规则去执行handler,故编写handler时按HandlerAdapter要求去做,这样适配器才可正确执行handler;
视图解析器ViewResolver:根据逻辑视图解析成真正的视图(View对象)
视图View:View是一个接口,实现类支持不同的View类型(jsp,PDF,Excel...)
因个人学识有限,若上述内容某处叙述不当,请指教!