前言
Servlet
是JavaWeb
中最核心的组件.
Servlet
规范为JavaWeb
应用制定了对象模型:
- 请求对象(
ServletRequest
):Servlet
从该对象中获取来自客户端的请求信息. - 相应对象(
ServletResponse
):Servlet
通过该对象来生成响应结果. Servlet
配置对象(ServletConfig
): 当容器初始化一个Servlet
对象时,会向Servlet
提供一个ServletConfig
对象(对应web.xml
当中的<servlet>
元素),Servlet
通过该对象来获取初始化参数信息即ServletContext
对象.Servlet
上下文(ServletContext
):Servlet
通过该对象来访问容器为当前Web
应用提供的各种资源.
生命周期
JavaWeb
应用的生命周期是由Servlet
容器来控制的:
- 启动阶段: 加载
Web
应用的相关数据,创建ServletConfig
对象,对Filter
和一些Servlet
进行初始化. - 运行时阶段: 为客户端提供服务.
- 终止阶段: 释放
Web
应用所占用的各种资源.
启动阶段
Servlet
容器在启动JavaWeb
应用时,会完成如下操作:
- 把
web.xml
文件中的数据加载到内存中. - 为
JavaWeb
应用创建一个ServletContext
对象. - 对所有的
Filter
进行初始化 - 对那些需要在
Web
应用启动时就被初始化的Servlet
进行初始化
运行时阶段
这是JavaWeb
应用最主要的生命阶段,对应Servlet
接口的service
方法
终止阶段
Servlet
容器在终止时,会完成如下操作:
- 销毁
JavaWeb
应用中所有处于运行时状态的Servlet
- 销毁所有
Filter
- 销毁所有与
JavaWeb
应用相关的对象,如ServletContext
对象等,并且释放Web
应用所占的资源.
可视化
最常用的Servlet
容器之一Tomcat
提供了管理界面,可以通过界面来管理Servlet
容器的生命周期:
不过需要提前配置tomcat-user.xml
,添加一个角色为manager-gui
的用户信息,不同的Tomcat
版本可能需要配置的角色不同,比如6.X
可能需要配置manager
角色,主要根据界面提示来配即可,tomcat-user.xml
位于$CATALINA_HOME/conf/
目录下:
<tomcat-users>
<role rolename="manager-gui"/>
<user username="tomcat" password="tomcat" roles="manager-gui"/>
</tomcat-users>
Servlet的生命周期
初始化阶段
Servlet
的初始化阶段包括4个步骤:
-
Servlet
容器加载Servlet
类,把它的.class
文件中的数据读入到内存中 -
Servlet
容器创建ServletConfig
对象 -
Servlet
容器创建Servlet
对象 -
Servlet
容器调用init(ServletConfig)
方法,在GenericServlet
实现类的复写下,建立了Servlet
对象与ServletConfig
对象的关系:@Override public void init(ServletConfig config) throws ServletException { this.config = config; this.init(); }
通过
ServletConfig
可以得到ServletContext
对象.
在以下情况之一,Servlet
会进入初始化状态:
- 当前
Web
应用处于运行时阶段,特定Servlet
被客户端首次请求访问.多数Servlet
都会在这种情况下被启动 - 如果在
web.xml
中为Servlet
设置了<load-on-startup>
元素,那么当所属Web
应用启动时,该Servlet
就会初始化. - 当
Web
应用被重新启动时,Web
应用中的所有Servlet
都会在特定的时刻被重新初始化.
运行时阶段
当Servlet
容器接收到请求,会创建针对这个请求的ServletRequest
和ServletResponse
对象,然后调用相应Servlet
对象的service
方法.service()
根据ServletRequest
得到请求参数,经过处理后用ServletResponse
生成响应结果.
当Servlet
容器吧Servlet
生成的响应结果发送给了客户,Servlet
容器就会销毁ServletRequest
和ServletResponse
对象.
销毁阶段
当应用终止,会西安调用所有Servlet
对象的destroy
方法,然后再销毁这些Servlet
对象,同时销毁与Servlet
对象关联的ServletConfig
对象.
ServletContext与Web应用范围
启动一个JavaWeb
应用,会创建唯一的ServletContext
对象,销毁容器也会对应销毁上下文.
简单来说,ServletContext
是用来存放Web
应用范围内的共享数据的.但要注意多线程的问题,它不是线程安全的.
通过ServletContext::getInitParam
来获取.
ServletContextListener
在Servlet
中有一个ServletContextListener
接口,它能够监听ServletContext
对象的生命周期,实际上就是监听Web
应用的生命周期.
当Servlet
容器启动或终止Web
应用时,会触发ServletContextEvent
事件,该事件由ServletContextListener
处理:
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
public class ServletContextListener implements javax.servlet.ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext servletContext = sce.getServletContext();
//do something with servlet context...
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
ServletContext servletContext = sce.getServletContext();
//do something with servlet context...
}
}
Servlet API
接口与类 | 作用 | 生命周期 |
---|---|---|
Servlet 接口 |
1. 负责相应客户请求 2. GenericServlet 是Servlet 接口的通用实现,而HttpServlet 提供了与HTTP 协议相关的实现 |
1. 对于多数Servlet ,只有当客户端首次请求访问时,才会被容器初始化:对于少数被设置为随Web 应用启动时启动的Servlet ,将在容器启动时初始化.2. 当 Web 应用被终止时,所有运行中的Servlet 都被销毁 |
ServletRequest 接口HttpServletRequest 接口 |
1. 表示客户端请求 2. HttpServletRequest 接口表示HTTP 请求 |
1. 容器每次接收到来自客户端的要求访问特定Servlet 的请求,就会创建ServletRequest 对象2. 当服务器端相应请求完毕,容器就会销毁 ServletRequest 对象 |
ServletResponse 接口HttpServletResponse 接口 |
1. Servlet 通过ServletResponse 接口来生成响应结果2. Servlet 通过HttpServletResponse 接口来生成HTTP 响应结果 |
1. 容器每次接收到来自客户端的要求访问特定Servlet 的请求,就会创建ServletResponse 对象2. 当服务器端响应请求完毕,容器就会销毁 ServletResponse 对象 |
ServletConfig 接口 |
包含了Servlet 的初始化参数信息,并且与当前Web 应用的ServletContext 对象关联 |
1. 当容器初始化一个Servlet 时,先创建一个ServletConfig 对象,使Servlet 对象与这个ServletConfig 对象关联2. 容器销毁时随之销毁 |
ServletContext 接口 |
这是容器为每个Web 应用进行分配的大管家.Servlet 通过它来存取Web 应用范围内的共享数据,还可以通过它来访问Servlet 容器的各种资源 |
1. 当容器启动一个Web 应用时,会为它创建一个ServletContext 对象2. 当容器终止一个 Web 应用时,会销毁它的ServletContext 对象 |
转发和包含
Servlet
对象由Servlet
容器创建,并且Servlet
对象的service()
方法也由容器调用.一个Servlet
对象可否直接调用另一个Servlet
对象的service()
方法呢?答案是否定的,因为一个Servlet
对象无法获得另一个Servlet
对象的引用.
Web
应用在响应客户端的一个请求时,有可能响应过程很复杂,需要多个Web
组件共同写作,才能生成响应结果.尽管一个Servlet
对象无法直接调用另一个Servlet
对象的service()
方法,但Servlet
规范为Web
组价之间的写作提供了两种途径:
- 请求转发:先对客户请求做一些预处理操作,然后把请求转发给其他组件来完成包括生成响应结果在内的后续操作
- 包含:把其他组件生成的响应结果包含到自身的响应结果中
需要借助ServletContext
对象获取RequestDispatcher
,再调用相关方法(forward
或include
)来实现:
getServletContext()
.getRequestDispatcher("/another_servlet_path")
.forward(req, resp);