Servlet API 是你的 Java Web 程序与 Servlet 容器(例如,Tomcat)之间的『约定』。
注意
Servlet 容器有且不仅只有 Tomcat 一种。后续内容就不再强调 Servlet 容器和 Tomcat 之间的关系,但凡提到 Tomcat 容器的场景,使用其它的 Servlet 容器也是可以的。
这个约定归结起来就是,Tomcat 将 Servlet 类载入内存,并由 Tomcat 调用 Servlet 对象的具体的方法 。这些方法所需的参数也是由 Tomcat 准备并传入的。
简单来说就是一句话,你按照某种特定规则写好代码『放在这里等着』Tomcat 来调用。
Servlet 技术的核心是 Servlet 接口:
Servlet 接口
└── GenericServlet 抽象类
└── HttpServlet
你的 Servlet 类必须直接或间接实现的 Servlet 接口。通常,我们是继承 HttpServlet,从而间接实现 Servlet 接口。
// 直接实现 Servlet 接口
public class AServlet implements Servlet { ... }
// 间接实现 Servlet 接口
public class BServlet extends GenericServlet { ... }
// 间接实现 Servlet 接口
public class CServlet extends HttpServlet { ... }
在 web 项目运行期间,每个 Servlet 类最多只能有一个对象。它们都是『单例』的,它们都是(被动地)由 Tomcat 创建的。
如果你是直接实现的 Servlet 接口, Tomcat 在调用你的 Servlet 的 .service 方法时,会传入两个参数:
ServletRequest 其中封装了当前的 HTTP 请求,因此,Servlet 开发人员不必解析和操作原始的 HTTP 数据。
ServletResponse 表示对当前用户的 HTTP 响应,它使得将响应发回给用户变得十分容易。
如果你是间接实现的 Servlet 接口,本质上也是如此。
ServletRequest 和 ServletResponse 对象是『多实例』的。
对于每一个 WebApp,Tomcat 还会创建一个 ServletContext 实例,它也是『单例』的。这个对象中中封装了上下文的环境详情。
每个 Servlet 实例也都有一个封装 Servlet 配置的 ServletCongfig ,Servlet 和 ServletConfig 是『一一对应』的。
总结:一个 WebApp 在运行时,有:
1 个 ServletContext 实例
N 个 Servlet 实例 (取决于 Servlet 类的数量)
N 个 ServletConfig 实例 (取决于 Servlet 类的数量)
任意个 HTTPRequest / HTTPResponse 实例 (取决于用户请求的次数)
Servlet 接口中定义了 5 个方法:
# | 方法 | 说明 |
---|---|---|
1 | .init | 在 Servlet 第一次被请求时,被 Servlet 容器调用。 Tomcat 调用 .init 时,容器会传入一个 ServletConfig 对象。 |
2 | .service | 在每次用户发起请求时,被容器调用。 Tomcat 调用 .service 时,容器会传入代表用户请求和相应的 HTTPRequest 对象和 HTTPResponse 对象。 |
3 | .destroy | 在销毁 Servlet 时,被 Tomcat 调用。一般发生在卸载 WebApp 或关闭容器时。 |
4 | .getServletInfo | 这个方法返回一个用于描述 Servlet 的字符串。 |
5 | .getServlet | 这个方法用于返回由 Servlet 传给 .init 方法的 ServletConfig 对象。 |
.init、.service 和 .destroy 方法是 Servlet 的生命周期方法,另外两个方法是非生命周期方法。
GenericServlet 抽象类实现了 Servlet 接口,它为这些方法提供了默认的实现,并新增了一个 servletConfig 实例变量,用于在 init() 方法中将容器传入的 ServletConfig 对象保存起来。
HTTPServlet 在其父类 GenericServlet 的基础上进一步简化了实现了 Servlet 接口的工作。
HTTPServlet 有两个特性是 GenericServlet 所不具备的:
不用 Override service() 方法,而是 Override doGet()
或者 doPost()
方法。
使用 HttpServletRequest/HttpServletResponse,而非 ServletRequest/ServletResponse。
每当 Tomcat 调用你的 Servlet 的 service 方法时,它都会创建一对新的 Request 和 Response 对象传入其中。
Tomcat 何时会调用你的Servlet 的 service 方法?
getParameter()
方法是 ServletRequest 中最常用的方法,该方法用于从 Request 对象中获取请求参数的值。
除了 getParameter()
外,类似用于获取请求参数的值的方法还有:
getParameterNames()
getParameterMap()
getParameterValues()
由于我们更长使用的是 HTTPServlet 类,而不是 Servlet 接口,因此,我们更多地是接触并使用 HttpServletRequest,而不是 ServletRequest 。
HTTPServletRequest 实现并扩展了 ServletRequest 接口。
HttpServletRequest 扩展的常用方法有:
Stirng getRequestURL( )
Stirng getRequestURI( )
Stirng getContextPath( )
String getMethod( )
Cookie[] getCookies( )
HttpSession getSession( )
Tomcat 在调用你的 Servlet 的 service()
/ doGet()
/ doPost()
方法时,除了会传入要给 Request 对象,还会传入一个 Response 对象:ServletResponse / HttpServletResponse 。
ServletResponse 隐藏了向浏览器发送响应的复杂过程。
在 ServletResponse 所有的方法中,最常用的方法之一是 getWriter()
方法,它返回一个可以向客户端发送文本的 java.io.PrintWriter
对象。默认情况下,PrintWriter 对象使用 ISO-8859-1 编码。
注意
有另外的一个向浏览器发送数据的方法叫getOutputStream()
,但这个方法是用于发送二进制数据的。因此大多数情况下使用的是getWriter()
,而非getOutPutStream()
。不要调用错了方法。
大多数情况下,你的 Servlet 通过 Tomcat 向客户端发送响应数据应该是一个 HTML 格式的字符串。
在发送这个 HTML 格式字符串前,应该先调用 setContentType()
方法,设置响应的内容类型,并将 text/html
作为参数传入。这是告诉浏览器,所发送给它的数据内容是 HTML 格式内容。
HTTPServletResponse 实现并扩展了 ServletResponse 接口。它所扩展的常用的方法有:
void addCookie ( Cookie cookie )
void sendRedirect ( String location )
当 Tomcat 创建出你的 Servlet 的单例对象后,它会调用你的 Servlet 的 init()
方法,并传入一个 ServletConfig 对象。
ServletConfig 对象中封装这由 @WebServlet
注解或者 部署描述符 传给 Servlet 的配置信息。
这样传入的每一条信息就叫做 初始化参数,一个初始化参数由 key 和 value 组成。
@WebServlet(name="HelloServlet",
urlPatterns = {"/hello.do"},
initParams = {
@WebInitParam(name="author", value="ben"),
@WebInitParam(name="email", value = "[email protected]")
})
为了获得 Servlet 的初始化参数,可以从容器传给 Servlet 的 ServletConfig 对象中调用 getInitParameter()
方法来获得。
ServletContext 代表着 WebbApp。每个 WebApp 只有一个 ServletContext 对象。
通过调用 ServletConfig 实例的 getServletContext() 方法,可以获得该 Servlet 所属的 WebApp 的 ServietContext 对象。
在 servlet 3.0 之前,不支持注解的形式来配置 servlet,而是在 web.xml
中使用配置描述符。
<servlet>
<servlet-name>HelloServletservlet-name>
<servlet-class>HelloServletservlet-class>
<init-param>
<param-name>authorparam-name>
<param-value>benparam-value>
init-param>
<init-param>
<param-name>emailparam-name>
<param-value>[email protected]param-value>
init-param>
servlet>
<servlet-mapping>
<servlet-name>HelloServletservlet-name>
<url-pattern>/HelloWorld/hello.dourl-pattern>
servlet-mapping>