掌握Servlet API 是成为一名技术高超的JAVA Web开发者的基础.必须非常熟悉Servlet API中所定义的70多种类型.
本篇博客将介绍Servlet API,并教你编写第一个Servlet应用程序.
Servlet API中有4个Java包,包括:
Servlet技术的核心是Servlet接口,这是所有Servlet类都必须直接或者间接实现的一个接口.Servlet接口定义了Servlet与Servlet容器之间的一个契约,Servlet容器会把Servlet类加载到内存中,并在Servlet实例中调用特定的方法.每个Servlet类型只能有一个实例.
Servlet容器还为每个应用程序创建一个ServletContext实例.这个对象封装context(应用程序)的环境细节.每个context只有一个ServletContext.每个Servlet实例还有一个封装Servlet配置信息的ServletConfig.
Servlet接口中定义了以下5个方法:
public void init(ServletConfig servletConfig)throws ServletException
public void service(ServletRequest request,ServletResponse response) throws ServletException,IOException
public void destroy()
public ServletConfig getServletConfig()
public String getServletInfo()
编写Java方法签名的规则是:与包含该方法的类型不在同一个包中的类型,要使用全类名.
init() service() destroy()方法是Servlet的生命周期方法,调用规则如下:
- init(): 第一次请求Servlet时,Servlet容器会调用这个方法.在后续的请求中不会在调用.
- service(): 每次请求Servlet时都会调用此方法,必须在这里编写要Servlet完成的响应代码. 第一次调用Servlet时会调用init()和service()方法,之后请求只调用service()方法.
- destroy(): Servlet要销毁时调用此方法,通常发生在卸载应用程序,或者关闭Servlet容器的时候.
- getServletInfo(): 该方法返回Servlet的描述,可以是任意字符串,甚至为null.
- getServletConfig(): 该方法返回由Servlet容器传给init方法的ServletConfig.为了让getServletConfig返回非null值,应该为传给init方法的ServletConfig赋给一个类级变量.
必须注意线程安全.一个应用程序中的所有用户将共用一个Servlet实例,因此 不建议使用类级变量,除非它们是只读的,或者是java.util.concurrent.atomic包中的成员.
Servlet应用程序编写起来非常简单,只需要创建一个目录结果,并将Servlet类放在某一个目录下即可.
在本节中,将学习如何编写一个简单的Servlet应用程序,将它命名为app01a.最初它只包含一个Servlet:MyServlet,其会给用户发送一条问候信息.
package app01a;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
@WebServlet(name = "MyServlet", urlPatterns = { "/my" })
public class MyServlet implements Servlet {
private transient ServletConfig servletConfig;
@Override
public void init(ServletConfig servletConfig)
throws ServletException {
this.servletConfig = servletConfig;
}
@Override
public ServletConfig getServletConfig() {
return servletConfig;
}
@Override
public String getServletInfo() {
return "My Servlet";
}
@Override
public void service(ServletRequest request,
ServletResponse response) throws ServletException,
IOException {
String servletName = servletConfig.getServletName();
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
writer.print(""
+ "Hello from " + servletName
+ "");
}
@Override
public void destroy() {
}
}
查看代码首先会注意到下面这个注解:
@WebServlet(name = “MyServlet”, urlPatterns = { “/my” })
注解中:name属性是可选的,一般用来提供Servlet类的名字,关键是urlPatterns它也是可选的,在MyServlet中,urlPatterns告诉容器,/my模式应该调用这个Servlet.
- URL模式必须一条正斜线开头.
Servlet应用程序必须以特定目录结果进行部署,应用程序包含WEB-INF目录
它有两个子目录:
- classes目录:Servlet类和其他的Java类都必须放在这里.类下方的目录反映了类的包结构.
- lib目录:在这里部署Servlet应用程序所需要的Jar文件.
所有的图像文件可以放在一个image目录下,所有的JSP页面可以放在一个jsp目录下,以此类推.
放在应用程序目录下的任何资源,用户可以通过输入该资源的URL而直接进行访问.如果希望某个资源可以被Servlet访问,但是不能被用户访问,那么应该把它放在WEB-INF目录下面.
把应用程序部署到Tomcat中:
运行MyServlet应用程序,通过以下地址:
http://localhost:8080/app01a/my
允许结果:
恭喜!你的第一个Servlet应用程序已经运行!
下面是ServletRequest接口中的部分方法:
public int getContentLength()
返回请求主体中的字节数,如果不知道字节长度,则返回-1;
public java.lang.String getContentType()
返回请求主体的MIME类型,如果不知道类型,则返回null;
public java.lang.String getProtocol()
返回这个HTTP请求的协议名称和版本号;
public java.lang.String getParameter(java.lang.String name)
返回指定请求参数的值,返回一个HTML表单域的值,获取查询字符串的值.如果不存在返回null;
利用getParameterNames getParameterMap和getParameterValues来获取表单域的名称和值,以及查询字符串.
ServletResponse中定义了getWriter()方法,它返回可以将文本传给客户端的java.io.PrintWriter.默认情况下,PrintWriter对象采用ISO-8859-1编码.
在发送任何HTML标签之前,先通过调用setContentType来设置响应的内容类型,比如:将text/html作为参数传递,告诉浏览器内容类型为HTML.
- getInitParameter(java.lang.String name)
从一个Servlet内部获取某个初始参数的值,由传入init方法的ServletConfig中调用getInitParameter.
- java.util.Enumeration getInitParameterNames()
返回所有初始参数名称的一个Enumeration.
- getServletContext()
从Servlet内部获取ServletContext.
举个ServletConfig的例子.添加一个ServletConfigDemoServlet:
package app01a;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
@WebServlet(name = "ServletConfigDemoServlet",
urlPatterns = { "/servletConfigDemo" },
initParams = {
@WebInitParam(name="admin", value="Harry Taciak"),
@WebInitParam(name="email", value="[email protected]")
}
)
public class ServletConfigDemoServlet implements Servlet {
private transient ServletConfig servletConfig;
@Override
public ServletConfig getServletConfig() {
return servletConfig;
}
@Override
public void init(ServletConfig servletConfig)
throws ServletException {
this.servletConfig = servletConfig;
}
@Override
public void service(ServletRequest request,
ServletResponse response)
throws ServletException, IOException {
ServletConfig servletConfig = getServletConfig();
String admin = servletConfig.getInitParameter("admin");
String email = servletConfig.getInitParameter("email");
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
writer.print("" +
"Admin:" + admin +
"
Email:" + email +
"");
}
@Override
public String getServletInfo() {
return "ServletConfig demo";
}
@Override
public void destroy() {
}
}
ServletContext表示Servlet应用程序.每个Web应用程序只有一个context.
用ServletConfig中调用getServletContext方法可以获得ServletContext.
保存在ServletContext中的对象称作属性.
ServletContext中包含如下常用属性:
- getAttribute()
- getAttributeNames()
- setAttribute()
- removeAttribute()
通过Servlet接口编写Servlet很麻烦,需要把它的所有方法都实现及时不用的方法,所以,我们有GenericServlet抽象类,它实现了Servlet和ServletConfig,并做了以下工作:
- 将init方法中的ServletConfig赋给一个类级变量,使它可以通过调用getServletConfig来获取.
- 为Servlet接口中的所有方法提供默认实现.
- 提供方法来包装ServletConfig中的方法.
在类中如果覆盖了init()方法,则必须调用super.init(servletConfig)来保存ServletConfig.
可以通过覆盖无参的init方法来编写初始化代码,ServletConfig仍然由GenericServlet实例保存.
接下来举个继承GenericServlet的Servlet栗子:
package app01a;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.GenericServlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
@WebServlet(name = "GenericServletDemoServlet",
urlPatterns = { "/generic" },
initParams = {
@WebInitParam(name="admin", value="Harry Taciak"),
@WebInitParam(name="email", value="[email protected]")
}
)
public class GenericServletDemoServlet extends GenericServlet {
private static final long serialVersionUID = 62500890L;
@Override
public void service(ServletRequest request,
ServletResponse response)
throws ServletException, IOException {
ServletConfig servletConfig = getServletConfig();
String admin = servletConfig.getInitParameter("admin");
String email = servletConfig.getInitParameter("email");
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
writer.print("" +
"Admin:" + admin +
"
Email:" + email +
"");
}
}
javax.servlet.http包是Servlet API中的第二个包,其包含了编写Servlet应用程序的类和接口.
HttpServlet类覆盖javax.servlet.GenericServlet类.
HttpServlet中有两项特性是GenericServlet所没有的:
- 不覆盖service方法,而是覆盖doGet doPost,或者两者都覆盖掉.在极少数情况下,还要覆盖以下某个方法:doHead() doPut() doTrace() doOptions()或doDelete().
- 将用HttpServletRequest和HttpServletResponse代替ServletRequest和ServletResponse.
HttpServletRequest表示HTTP环境中的Servlet请求.它继承java.servlet.ServletRequest接口,并增加了几个方法:
- String getContextPath()
返回表示请求context的请求URI部分.
- Cookie[] getCookies()
返回一个Cookie对象数组.
- String getHeader(String name)
返回指定HTTP标头的值.
- String getMethod()
返回发出这条请求的HTTP方法的名称.
- String getQueryString()
返回请求URL中的查询字符串.
- HttpSession getSession()
返回与这个请求有关的session对象.如果没有找到,则创建新的session对象.
- HttpSession getSession(boolean create)
返回与这个请求有关的session对象.如果没有找到,并且create参数为true,那么将创建新的session对象.
HttpServletResponse表示HTTP环境下的Servlet响应.
- void addCookie(Cookie cookie) 给这个响应对象添加cookie
- void addHeader(String name,String value) 给这个响应对象添加标头.
- void sendRedirect(String location) 发送响应代号,将浏览器重定向到指定的位置.
每个Web应用程序中几乎都会包含一个或者多个HTML表单,用来接收用户输入.可以轻松地将一个HTML表单从Serlet发送到浏览器.
HTML输入域或文本域中的值将被当作字符串发送到服务器.空白的输入域或文本域将发送一条空白的字符串,因此,带有一个输入域名称的ServletRequest.getParameter将永远不会返回null.
package app01b;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(name = "FormServlet", urlPatterns = { "/form" })
public class FormServlet extends HttpServlet {
private static final long serialVersionUID = 54L;
private static final String TITLE = "Order Form";
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
writer.println("");
writer.println("");
writer.println("" + TITLE + "");
writer.println(""
+ TITLE + "");
writer.println(");
writer.println("");
writer.println("");
writer.println("Name: ");
writer.println(" ");
writer.println(" ");
writer.println("");
writer.println("Address: ");
writer.println(" ");
writer.println(" ");
writer.println("");
writer.println("Country: ");
writer.println(" ");
writer.println(" ");
writer.println("");
writer.println("Delivery Method: ");
writer.println(" +
"name='deliveryMethod'"
+ " value='First Class'/>First Class");
writer.println(" +
"name='deliveryMethod' "
+ "value='Second Class'/>Second Class ");
writer.println(" ");
writer.println("");
writer.println("Shipping Instructions: ");
writer.println(" ");
writer.println(" ");
writer.println("");
writer.println(" ");
writer.println(" ");
writer.println(" ");
writer.println("");
writer.println("Please send me the latest " +
"product catalog: ");
writer.println(" +
"name='catalogRequest'/> ");
writer.println(" ");
writer.println("");
writer.println(" ");
writer.println("" +
" ");
writer.println(" ");
writer.println("
");
writer.println("");
writer.println("");
writer.println("");
}
@Override
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
writer.println("");
writer.println("");
writer.println("" + TITLE + "");
writer.println("");
writer.println(""
+ TITLE + "");
writer.println("");
writer.println("");
writer.println("Name: ");
writer.println("" + request.getParameter("name")
+ " ");
writer.println(" ");
writer.println("");
writer.println("Address: ");
writer.println("" + request.getParameter("address")
+ " ");
writer.println(" ");
writer.println("");
writer.println("Country: ");
writer.println("" + request.getParameter("country")
+ " ");
writer.println(" ");
writer.println("");
writer.println("Shipping Instructions: ");
writer.println("");
String[] instructions = request
.getParameterValues("instruction");
if (instructions != null) {
for (String instruction : instructions) {
writer.println(instruction + "
");
}
}
writer.println(" ");
writer.println(" ");
writer.println("");
writer.println("Delivery Method: ");
writer.println(""
+ request.getParameter("deliveryMethod")
+ " ");
writer.println(" ");
writer.println("");
writer.println("Catalog Request: ");
writer.println("");
if (request.getParameter("catalogRequest") == null) {
writer.println("No");
} else {
writer.println("Yes");
}
writer.println(" ");
writer.println(" ");
writer.println("
");
writer.println("");
writer.println("Debug Info
");
Enumeration parameterNames = request
.getParameterNames();
while (parameterNames.hasMoreElements()) {
String paramName = parameterNames.nextElement();
writer.println(paramName + ": ");
String[] paramValues = request
.getParameterValues(paramName);
for (String paramValue : paramValues) {
writer.println(paramValue + "
");
}
}
writer.println(" ");
writer.println("");
writer.println("