Tomcat与Servlet
这同样是个老生常谈的话题,但是看再多也没有自己梳理一遍清楚,而且每过一遍,对于容器的认知就更加清晰,要知道,框架其实就是将这些底层的东西就行了封装,实际上用来用去的还是这些东西。
这一篇主要梳理一下:
Tomcat容器
Servlet容器
容器的交互
1.Tomcat容器
Tomcat在开发之中主要作为web容器,其本身完全由Java代码编写,可以用来作为servlet的容器,并且提供一些额外的功能,例如:Tomcat管理和控制平台,安全域管理,tomcat阀等
当它作为servlet容器的基本功能是负责接收和解析来着客户的请求
Tomcat的作用在这一篇博客里面有比较清楚的梳理:(至于更细节的流程等把操作笔记整理出来再详细说说)
Tomcat
这里就不重复介绍了。
总的来说,tomcat就是一个大的Web容器,来装载其他的容器
那么现在就剩下Servlet了。
2.Servlet
这一篇会将Servlet及其容器以及流程尽量的梳理。
2.1 JSP与Servlet
提到Servlet就不得不提到JSP,他们两者的关系大概说一下:
对比 | Servlet | JSP |
---|---|---|
不同 | Servlet在Java代码中通过HttpServletResponse对象动态输出HTML内容 | JSP在静态HTML内容中嵌入Java代码,Java代码被动态执行后生成HTML内容 |
相同 | Servlet能够很好地组织业务逻辑代码,但是在Java源文件中通过字符串拼接的方式生成动态HTML内容会导致代码维护困难、可读性差 | JSP虽然规避了Servlet在生成HTML内容方面的劣势,但是在HTML中混入大量、复杂的业务逻辑同样也是不可取的 |
使用方式 | Servlet往往作为承担客户请求和业务处理的中间角色 | JSP往往通过调用JavaBean以及自身Java代码配合HTML实现业务 |
他们有各种的优缺点,在MVC的配合下达到一个一个很好的协调,以下是配合MVC的流程,间接说明两者的关系,这个后面还会详细说明
Created with Raphaël 2.1.2Web浏览器发送HTTP请求到服务端,被Controller(Servlet)获取并进行处理(例如参数解析、请求转发)Controller(Servlet)调用核心业务逻辑——Model部分,获得结果Controller(Servlet)将逻辑处理结果交给View(JSP),动态输出HTML内容动态生成的HTML内容返回到浏览器显示
2.2 web请求
web请求要和Servlet分开说,这是两个非常大的概念
Created with Raphaël 2.1.2输入地址www.baidu.comDNS域名解析(这里面还设计本地DNS解析以及未查询到向底层解析查询的过程,这个整理好再发)建立TCP连接(TCP连接三次握手等)发送请求Web服务器上Ngnix进行反向代理映射到应用服务器进行Servlet请求处理(包括对HttpResponse等一系列Servlet内部的操作)用户浏览器渲染页面
这就是通常而言外部的WEB请求的外部流程,不涉及内部HttpRequest及HttpResponse的流程。
现在,请求就已经到了Tomcat上面了,Tomcat接收到请求又会发生什么
2.3 servlet容器及初始化
现在说说Servlet中几个比较重要的部分:
与Servlet有关联的重要类主要有三个:
- ServletConfig:在请求初始化的时候被调用,用于配置Servlet
- ServletRequest:在请求到达的时候被调用
- ServletResponse:在请求到达的时候被调用
Servlet是典型的握手式交互,在一个专门的场所进行交互,这个就是Tomcat,更详细的说就是Tomcat 的Context容器,context容器中的每一个Wrapper就是一个Servlet.
那么现在就针对于Wrapper来详细的说说Servlet容器:
tomcat的具体实现是StandardWrapper类,这个类的作用是:加载它表示的servlet并分配它的一个实例。
那么,StandardWrapper具体是怎么加载这个Servlet的呢?
一般Servlet的初始化有三个可能:
参数 | 解释 |
---|---|
load-on-startup < 0 | 此时为负数,web容器启动的时候不做实例化处理,servlet首次被调用时做实例化 |
load-on-startup > 0 | web容器启动的时候做实例化处理,按照顺序由小到大处理,正整数小的先被实例化 |
load-on-startup = 0 | web容器启动的时候做实例化处理 |
不论是哪一种,我们都从Servlet准备实例化开始:
在 Servlet API 中有一个 ServletContextListener 接口,它能够监听 ServletContext 对象的生命周期,实际上就是监听 Web 应用的生命周期。
当Web 应用启动或终止时,会触发ServletContextEvent 事件,该事件由 ServletContextListener 来处理。
在 ServletContextListener 接口中定义了处理ServletContextEvent 事件的两个方法。
contextInitialized(ServletContextEvent event)和contextDestroyed(ServletContextEvent event)方法,对别 在Servlet 容器启动Web 应用时和终止Web 应用时调用。
• Servlet容器启动时,触发servletContextEvent事件,并通知相应的监听器servletContextListener;
• Servlet容器在启动的过程中通过servletContextListener监视servletContext的状态(初始化或者销毁servletContext);即 • servletContextListener中通过contextInitialized初始化方法,根据web.xml对servletContext进行配置,即将Context容器的属性缓存在内存中,供Service服务利用;
contextConfig在Tomcat创建Context容器时被加入到servletContext中,contextConfig负责整个Web应用的配置文件(包括web.xml)的解析工作。
以上是Servlet的大框架布置,然后,会一步步的布置小装饰。
此时servlet配置了初始化参数,web容器在创建servlet实例对象时,会自动将这些初始化参数(通常在WEB.xml中)封装到ServletConfig对象(javax.servlet.ServletConfig 接口的实现)中,并在调用servlet的init方法时,将ServletConfig对象传递给Servlet。
注意,此过程在Servlet实例的生命周期里只调用一次。
进而,程序员通过Servlet对象得到当前servlet的初始化参数信息。
以上就是Servlet的初始化
那么,我们继续深入一点点:这些的资料不足,可能有错误。。。(只是一种思路,有时间会详细看看源码分析一下)
ServletContext / servletContextFactory
StandardWrapper / StandardWrapperFactory
让我们再来看看这一张图:
WEB容器在启动时,它会为每个WEB应用程序都创建一个对应的ServletContext对象,它代表当前web应用。所以,多个WEB容器应该有多个ServletContext对象。
我们通过:
public ServletContext getServletContext();
获取的实际上就是一个ApplicationContextFacade对象,此对象对ApplicationContext做了封装,而ApplicationContext含有Tomcat的context实例(StandardContext)
StandardWrapperFactory和 servletContextFactory是工厂类,用来产生StandardWrapper和ServletContext 。
而ServletContext配置多个Servlet的基础信息,而StandardWrapper 用来配置一个Servlet的专属属性。
再以下是我从笔记本中整理的:多年以前的,已不可考,也没搜索到相关的文档。。。
StandardWrapper,StandardWrapperFactory均是ServletConfig的子类,是Tomcat对于Servlet的一种解耦方式,将Servlet独立于Tomcat之外的一种方式。
2.4 servlet流程
实现,Tomcat会将HttpServletRequest及HttpServletResponse请求传递给servlet,并且被Servlet处理,具体的转换流程先不说。(有时候我们通常把Tomcat叫做Servlet容器,但是,HttpServlet并不是servlet产生,而是Servlet容器即Tomcat产生,这点在代码中有体现,servlet接收到的已经是被转换过的ServletRequest req, ServletResponse res)(个人理解。。。)
//HttpServlet 的 service()
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
if (!(req instanceof HttpServletRequest &&
res instanceof HttpServletResponse)) {
throw new ServletException("non-HTTP request or response");
}
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
service(request, response);
}
首先:Servlet由两个Java包组成:Javax.servlet和Javax.servlet.http
- Javax.servlet:定义了所有Servlet类都必须实现或者扩展的通用接口和类
- Javax.servlet.http:定义了采用Http协议通信的HttpServlet
先看看Servlet中的流程
Created with Raphaël 2.1.2Tomcat将请求转发到ServletServletContext接收到请求ServletContext创建一个HttpRequest,同时创建一个HttpResponse对象ServletContext调用Servelet的Service,将HttpResponse和HttpRequest对象传递给ServletHttpServlet调用HttpRequest的有关方法对请求进行分析处理完成后续业务流程返回给客户端
Servlet处理请求
Servlet被初始化之后,它已经可以处理来自客户端的请求,每一个来自客户端的请求都被描述成一个ServletRequest对象,Servlet的响应被描述成一个ServletResponse对象。
当客户端发出请求时,Servlet引擎传递给Servlet一个ServletRequest对象和一个ServletResponse对象,这两个对象作为参数传递到service()方法中。
ervlet 也可以执行ServletRequest接口和ServletResponse接口。ServletRequest接口使得Servlet有权使用客户端发出的请求。Servlet可以通过ServletInputStream对象读取请求信息。
ServletResponse接口允许Servlet建立响应头和状态代码。通过执行这个接口,Servlet有权使用ServletOutputStream类来向客户端返回数据。
提到流程就必须要提到生命周期:
Servlet的生命周期:
声明周期 | 主要职能 |
---|---|
init() | 初始化Servlet实例,并且向他传送一个javax.servlet.ServletConfig接口的对象给他。该对象用于读取Web.xml中文件中设置的初始值 |
service() | 处理每一个请求,并且定义了能处理的请求类型以及调用合适的方法来处理这些请求类型(需要实现这些方法,doget,dopost等) |
destory() | 销毁对象 |
2.5 servlet的实现方式
实现Servlet一般有三种方式:
- 实现Servlet接口,然后实现Servlet接口中的五个方法
- 继承GenericServlet类,然后实现Service()方法
- 继承HttpServlet类,重写doget(),dopost()
Servlet中的五个方法为:
方法 | 作用 |
---|---|
public void init(ServletConfig config) throws ServletException; | 初始化 |
public ServletConfig getServletConfig(); | 获取Servlet对象的配置参数 |
public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException; | 用于处理客户端请求 |
public String getServletInfo(); | 返回有关Servlet的信息 |
public void destroy(); | 在容器中销毁Servlet,释放资源(注意,不是销毁容器,分清楚容器和Servlet的关系) |
不是doget,dopost那几个啊!!
2.6 servlet的监听器
Servlet监听器
2.7 servlet的过滤器
Servlet 过滤器方法
过滤器是一个实现了 javax.servlet.Filter 接口的 Java 类。javax.servlet.Filter 接口定义了三个方法:
方法 | 作用 |
---|---|
public void doFilter (ServletRequest, ServletResponse, FilterChain) | 该方法完成实际的过滤操作,当客户端请求方法与过滤器设置匹配的URL时,Servlet容器将先调用过滤器的doFilter方法。FilterChain用户访问后续过滤器。 |
public void init(FilterConfig filterConfig) | web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,读取web.xml配置,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(filter对象只会创建一次,init方法也只会执行一次)。开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。 |
public void destroy() | Servlet容器在销毁过滤器实例前调用该方法,在该方法中释放Servlet过滤器占用的资源。 |
Filter的使用包括两个部分:
1.web.xml中定义的filter过滤器
2.在后台实现的相应的filter对象
这个例子里面利用相应filter对象的init方法,利用自带的filterconfig方法获得之前设定的初始值 site
要理解filter的作用是为了在讲请求完全传到后台之前进行一些操作
<filter>
<filter-name>LoginFilter</filter-name>
<filter-class>com.runoob.test.LogFilter</filter-class>
<init-param>
<param-name>Site</param-name>
<param-value>mySite</param-value>
</init-param>
</filter>
// 在 init 方法使用 FilterConfig 对象获取参数:
public void init(FilterConfig config) throws ServletException {
// 获取初始化参数
String site = config.getInitParameter("Site");
// 输出初始化参数
System.out.println("网站名称: " + site);
}
//实现 Filter 类
public class LogFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
// 获取初始化参数
String site = config.getInitParameter("Site");
// 输出初始化参数
System.out.println("网站名称: " + site);
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException {
// 输出站点名称
System.out.println("站点网址:http://www.runoob.com");
// 把请求传回过滤链
chain.doFilter(request,response);
}
public void destroy( ){
/* 在 Filter 实例被 Web 容器从服务移除之前调用 */
}
}
filter的特点:
(一定要实现javax.servlet包的Filter接口的三个方法init()、doFilter()、destroy(),空实现也行)
(1)、启动服务器时加载过滤器的实例,并调用init()方法来初始化实例;
(2)、每一次请求时都只调用方法doFilter()进行处理;
(3)、停止服务器时调用destroy()方法,销毁实例。
整个流程就行将前台传递的请求先用filter进行处理,然后传递到servlet进行逻辑控制,但是通常在开发之中这种原生的JSP写法已经不多了,我们会用框架来完成我们的需求。这样更加简单。
3.Servlet类小结
名称 | 作用 |
---|---|
Request Dispatcher接口 | 定义一个对象,从客户端接收请求,然后将它发给服务器的可用资源(例如Servlet、CGI、HTML文件、JSP文件)。Servlet引擎创建request dispatcher对象,用于封装由一个特定的URL定义的服务器资源。 这个接口是专用于封装Servlet的,但是一个Servlet引擎可以创建request dispatcher对象用于封装任何类型的资源。 |
Servlet接口 | 这个接口定义了一个Servlet:一个在Web服务器上继承了这个功能的Java类。 |
ServletConfig接口 | 这个接口定义了一个对象,通过这个对象,Servlet引擎配置一个Servlet并且允许Servlet获得一个有关它的ServletContext接口的说明。每一个ServletConfig对象对应着一个唯一的Servlet。 |
ServletContext接口 | 定义了一个Servlet的环境对象,通过这个对象,Servlet引擎向Servlet提供环境信息。 一个Servlet的环境对象必须至少与它所驻留的主机是一一对应的。在一个处理多个虚拟主机的Servlet引擎中(例如,使用了HTTP1.1的主机 头域),每一个虚拟主机必须被视为一个单独的环境。此外,Servlet引擎还可以创建对应于一组Servlet的环境对象。 |
ServletRequest接口 | 定义一个Servlet引擎产生的对象,通过这个对象,Servlet可以获得客户端请求的数据。这个对象通过读取请求体的数据提供包括参数的名称、值和属性以及输入流的所有数据。 |
ServletResponse接口 | 定义一个Servlet引擎产生的对象,通过这个对象,Servlet对客户端的请求作出响应。这个响应应该是一个MIME实体,可能是一个HTML页、图象数据或其他MIME的格式。 |
SingleThreadModel接口 | 这是一个空接口,它指定了系统如何处理对同一个Servlet的调用。如果一个Servlet被这个接口指定,那么在这个Servlet中的service方法中将不会有两个线程被同时执行。 |
GenericServlet类 | 这个类的存在使得编写Servlet更加方便。它提供了一个简单的方案,这个方案用来执行有关Servlet生命周期的方法以及在初始化时对ServletConfig对象和ServletContext对象进行说明 |
ServletInputStream类 | 这个类定义了一个用来读取客户端的请求信息的输入流。这是一个Servlet引擎提供的抽象类。一个Servlet通过使用ServletRequest接口获得了对一个ServletInputStream对象的说明。 |
ServletOutputStream类 | 这是一个由Servlet引擎使用的抽象类。Servlet通过使用ServletResponse接口的使用获得了对一个这种类型的对象的说明。利用这个输出流可以将数据返回到客户端。 |
ServletException | 构造一个新的ServletException,如果这个构造函数包括一个Throwable参数,这个Throwable对象将被作为可能抛出这个异常的原因。 |
HttpServletRequest接口 | 用来处理一个对Servlet的HTTP格式的请求信息。 |
HttpServletResponse接口 | 描述一个返回到客户端的HTTP回应。这个接口允许Servlet程序员利用HTTP协议规定的头信息。 |
HttpSession接口 | 这个接口被Servlet引擎用来实现在HTTP客户端和HTTP会话两者的关联。这种关联可能在多外连接和请求中持续一段给定的时间。session用来在无状态的HTTP协议下越过多个请求页面来维持状态和识别用户。 一个session可以通过cookie或重写URL来维持。 |
HttpSessionBindingListener接口 | 这个对象被加入到HTTP的session中,执行这个接口会通告有没有什么对象被绑定到这个HTTP session中或被从这个HTTP session中取消绑定。 |
HttpSessionContext接口 | 这个接口由于安全的原因被取消,它出现在目前的版本中仅仅是为了兼容性的原因。这个接口的方法将模拟以前的版本的定义返回相应的值。 |
Cookie类 | 这个类描述了一个cookie,有关cookie的定义你可以参照Netscape Communications Corporation的说明,也可以参照RFC 2109。 |
HttpServlet类 | 这是一个抽象类,用来简化HTTP Servlet写作的过程。它是GenericServlet类的扩充,提供了一个处理HTTP协议的框架。 |
HttpSessionBindingEvent类 | 这个事件是在监听到HttpSession发生绑定和取消绑定的情况时连通HttpSessionBindingListener的。这可能是一个session被终止或被认定无效的结果。 |
HttpUtils类 | 收集HTTP Servlet使用的静态的有效的方法。 |
4.总结
Servlet就这么简单的梳理一下,有空会将Serlvet的详细源码进行分析
参考:
https://www.zhihu.com/question/37962386
还没有评论,来说两句吧...