本文回顾一下Servlet。
1. 概述
一般情况下,Web应用就是通过网站来访问的,基于HTTP协议。那么服务端接收请求,则需要解析数据包,但是解析数据包显然程序员不能做,因此,出现了各种各样的Web服务器,负责接收数据包,并解析提取参数。解析参数则依据HTTP协议的规定。
但是仅仅提取URL以及参数还是不够的,因为后端程序员主要是负责业务功能的实现,所以必须还要知道依据这些URL调用指定的方法并传参,返回给客户端。因此这些依据就是Servlet规范。
可以看到Web服务器就是一座桥梁,负责连通两岸,而桥梁和两岸的连接处就是HTTP协议标准以及Servlet规范。
那么怎么知道某个URL来调用哪个方法呢?通过配置文件servlet-mapping来映射。找到这个类之后,就创建对象并调用该类的service方法来提供服务。
2. Servlet接口
Servlet接口有5个方法。
1 | public interface Servlet{ |
分别是初始化、对外服务、销毁、获取Servlet信息、获取Servlet配置。
我们知道,调用方法提供服务,显然是需要先有该对象的,然后才能调用方法。那么什么时候创建Servlet对象呢?
2.1 Servlet对象的创建
默认情况下,Web服务器启动是不会创建Servlet对象,只有在被调用时,才会创建该对象。并放到一个哈希表中,后续再次需要时,则会直接调用该对象。可以设置Web服务器启动时就创建某个对象。
2.2 Servlet接口方法的调用
- 创建对象,首先会调用无参构造方法。【只会调用一次】
- 创建好对象后,立马执行init初始化方法。【只会调用一次】
- 当对应的请求到达后,会调用service方法。【每次请求都会调用一次】
- 当对象销毁时,比如服务器关闭,会调用destroy方法。【只调用一次,通常进行资源的关闭】
- getServletInfo()用于获得Servlet的相关信息,比如作者、版本等。
- getServletConfig()用于获得ServletConfig对象,即获取配置信息。
注意,上述方法都是由Web服务器调用,所以参数都是由Web服务器创建的。
3. 适配器模式封装Servlet接口
可以看到,上面的方法只有service方法是最常用的,也是最经常出现的,而其他方法则很少使用。因此,不应该每次实现Servlet接口都要实现其余四个方法。
因此,可采用适配器模式,实现Servlet接口,对于不常用的抽象方法予以实现;而对于常用的方法保留,用于子类继承实现。适配器其实就是适配不同的需求,从而保留不同的方法。
Servlet中已经实现了适配器类,即javax.servlet.GenericServlet
类。而HttpServlet则是专门用于HTTP协议准备的抽象类,该类继承了GenericServlet。
4. ServletConfig接口
ServletConfig接口是作为上面Servlet的方法的参数类型,其实就是Servlet对象的配置信息所封装的对象。
配置信息其实就是web.xml配置文件中<servlet>标签中的信息。主要有以下几个方法:
方法名 | 描述 |
---|---|
String getInitParameter(String name) | 获取指定name的初始化配置信息。(注意,在web.xml中是可以通过<init-para>标签来设置servlet的初始化信息的) |
Enumeration<String> getInitParameterNames() | 获取所有初始化信息的name。 |
ServletContext getServletContext() | 获得ServletContext对象 |
String getServletName() | 获得Servlet对象的名字 |
实际上GenericServlet类已经实现了ServletConfig接口,所以在service方法中,直调用this就可调用上面的四个方法。
5. ServletContext接口
上面Servlet接口以及ServletConfig接口,还有一个ServletContext接口类型。我们知道,ServletConfig指的就是在web.xml文件中,<servlet>标签对该类的配置信息。ServletContext接口其实就是提供了对整个web.xml的封装对象,通过ServletContext对象,可获取到所有web.xml中所有的配置信息,包括创建的对象。
方法名 | 描述 |
---|---|
String getInitParameter(String name) | 获得指定name的全局配置信息 |
Enumeration<String> getInitParameterNames() | 获得全局配置信息的所有name |
String getContextPath() | 获取所在webapp项目的根路径 |
String getRealPath(String path) | 获取path的绝对路径,这里的path指的是在webapp中的路径,以web为根路径,如/path.html. |
void log(String msg) | 将指定的信息写入日志文件,日志在Tomcat安装目录下的logs目录下 |
void log(String message, Throwable throwable) | 在上述方法的基础上,也将异常信息写入进去 |
void setAttribute(String name, Object object) | 向应用域中存数据(应用域的概念可参考5.3节) |
void getAttribute(String name) | 向应用域中取数据 |
void removeAttribute(String name) | 删除应用域中的数据 |
从某种程度上说,这个web.xml是全局配置信息,包含了创建的对象,也就是是一块空间,被称为应用域,ServletContext接口的实现类对象就是应用域。可以向应用域对象中存储数据,即整个应用的共享变量,这个数据是放在缓存中的。
6. HttpServlet
上面讲到了适配器模式,就是适配不同的需求,而保留不同的方法。其实这里还有一个模板方法模式。可以看成是一个框架,核心的执行流程已经固定写好,比如一天的流程依次是起床,工作,午饭,工作,睡觉等等。但是具体的工作内容,午饭菜品等等可自定义由子类实现。
比如上面的Servlet,只要有请求来了,就会找该Servlet对象,如果找到就调用service方法,找不到就创建对象,初始化,调用service方法。但是service方法具体实现,可自定义。
HttpServlet是专门为HTTP协议制定的一个抽象类,继承自GenericServlet。因为HTTP协议有很多种请求方式,比如put、post等等。而每种请求方式,所处理的内容、处理方式以及获取参数是不同的。
因此,HttpServlet继承了GenericServlet之后,在核心service方法中,进行判断,从而调用不同的自己实现的方法,但是自己实现的方法并没有执行具体任务,只是输出了语句【具体功能实现由程序员自定义子类实现】,这就是模板方法模式。
1 | protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { |
7. HttpServletRequest
其实Servlet接口中,还有ServletRequest和ServletResponse两个接口类型没说。和HttpServlet一样,HttpServletRequest继承ServletRequest,专门为了Http协议而建立的接口。
HttpServletRequest接口的实现类对象被称为请求域对象,和应用域对象类似,只不过作用范围只在一次请求中有效。
8. Cookie和Session
一次请求到达之后,服务器会获取请求的Cookie对象,如果没有,则为其创建一个Cookie对象,并把Cookie对象作为sessionid,创建一个Session对象【会话域】。这样,一个Session对象可以对应多次request请求。可以使得多次请求之间通信。
9. Listener监听器
监听器(Listener)是Servlet规范的一种扩展,是一组来自Servlet规范下的接口,共有9个接口。监听器接口用于监控【作用域对象生命周期变化时刻】以及【作用域对象共享数据变化时刻】,比如session对象的创建和销毁以及所存储数据的变化情况。
前面提到过,实际上数据库操作中最耗时的就是连接对象的创建,即二者的I/O通道创建比较耗时,所以为了提升效率,可以在服务器启动的时候创建好一批连接对象(数据库连接池,用的时候取一个,用完之后再放回),并且直到服务器关闭的时候再销毁连接对象。那么怎么知道服务器启动和关闭呢?可以监听ServletContext应用域,因为服务器启动的时候会先创建应用域对象,关闭的时候会先销毁应用域对象,那么监听二者,在二者事件触发的时候,就创建数据库连接对象以及销毁对象即可。
10. Filter过滤器
和监听器(Listener)一样,过滤器(Filter)也是来自于Servlet下的接口。过滤器主要是在Http服务器调用资源文件之前,对Http服务器进行拦截,即不是无限制的对所有请求都应答,设置一个过滤器,即增加一个门槛
开发过滤器实现类分为以下三步:
- 创建一个类实现Filter接口;
- 重写接口中的doFilter方法;
- 在web.xml配置文件中将实现类注册到WEB容器上。