什么是Servlet?
Servlet(Server Applet),全称是Java Servlet。Servlet技术是Sun公司提供一种实现动态网页的解决方案,它是基于Java编程语言的Web服务器端编程技术,主要用于在Web服务器端获得客户端的访问请求信息和动态生成对客户端的响应信息。一个Servlet程序就是一个实现了特殊接口的Java类,它由支持Servlet的Web服务器(具有Servlet引擎)调用和启动运行。一个Servlet程序负责处理它所应一个或一组URL地址的访问请求,并用于接收客户端发出的访问请求和产生响应内容。
Servlet程序具有以下的一些基本功能:
- 获取客户端通过HTML的Form表单递交的数据和URL后面的参数信息。
- 创建对客户端的响应消息内容。
- 访问服务器的文件文件系统。
- 连接数据库并开发基于数据库的应用。
- 调用其他Java类。
Servlet的工作模式
- 客户端发送请求至服务器。
- 服务器启动并调用Servlet,Servlet根据客户端请求生成响应内容并将其传给服务器。
- 服务器将响应放回给客户端。
ServletAPI
Sun公司定义了一套专门用于开发Servelt程序的Java类和接口,这些类和接口提供Servlet程序开发中涉及的各种功能,它们统称为ServletAPI(ServletApplication Programming Interface)。Servlet引擎与Servlet程序之间采用ServletAPI进行通信。
ServletAPI包含以下4个Java包:
- javax.servlet其中包含定义Servlet和Servlet容器之间契约的类和接口。
- java.servlet.http 其中包含定义HTTP Servlet和Servlet容器之间的关系。
- javax.servlet.annotation 其中包含标注Servlet,Filter,Listener的标注。它还为被标注元件定义元数据。
- javax.servlet.descriptor,其中包含提供程序化登录Web应用程序的配置信息的类型。
Servlet的主要类型
Servlet的使用方法
1.编写Servlet程序
Servlet技术的核心是Servlet,它是所有Servlet类必须直接或间接实现的一个接口。在编写实现Servlet的Servlet类时,直接实现它。在扩展实现这个接口的类时,间接实现它。
为了简化Servlet程序的编写 ,ServletAPI提供了一个实现Servlet接口的最简单的Servlet类,其完整名javax.servlet.GenericServlet,这个类实现了Servlet程序的基本特征和功能。
ServletAPI 还提供了了一个专用于HTTP协议的Servlet类,其名称为javax.servlet.http.HttpServlet,它是GenericServlet类的子类,在GenericServlet基础上进行了一些针对HTTP特点的扩充。
2.Servlet的注册与运行
Servlet程序必须通过Web服务器和Servlet引擎来启动运行。Servlet程序的存储目录有特殊要求,通常需要存储在<Web应用程序目录》\Web-INF\classes\目录中。Servlet程序必须在Web应用程序中的Web.xml文件进行注册和映射其访问路径,才可以被Servlet引擎加载和被外界访问。
注册Servelt:在Web.xml文件中,一个<servlet>元素用于注册一个Servlet。<servlet>元素包含了两个主要的子元素,分别用于设置Servlet的注册名称和指定Servlet的完整类名。
<servelt>
<servelt-name>...</servelt>
<servelt-class>...</servelt-class>
</servelt>
映射Servlet:一个<servlet-mapping>元素用于映射一个已经注册的Servlet的一个对外访问路径,客户端将使用Servlet所映射的对外访问路径来访问Servlet,而不是使用Servlet名称来访问Servlet。<servlet-mapping>包含两个子元素,分别用于指定Servlet的注册名称和设置Servlet的对外访问路径。
<servelt-mapping>
<servelt-name>...</servelt>
<url-pattern>...</url-pattern>
</servelt-mapping>
<url-pattern>子元素中的访问路径必须以正斜杠(/)开头,这个正斜杠表示当前Web应用程序的根目录,而不是整个Web站点的根目录。
Servlet的工作原理
Web服务器调用Servlet引擎来装载和解释执行相应的Servlet程序,Servlet进行处理后生成响应内容给Servlet引擎,然后由Web服务器发送给客户端。Web服务器返回给客户端的内容是Servlet的运行结果,Servlet自身的程序代码并不会传递到客户端。
Servlet接口中定义的方法
public interface Servlet {
void init(ServletConfig var1) throws ServletException;
ServletConfig getServletConfig(); //返回由Servelt容器传给init方法的ServeltConfig对象
void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
String getServletInfo(); //返回Servelt的一段描述,可以返回一段字符串
void destroy();
}
Servlet运行过程
Servlet引擎按照下面的过程来调用一个Servlet程序:
- 接收到访问某个Servlet的HTTP请求之后,Servlet引擎首先检查是否已经装载并创建了Servlet的实例对象。如果已经装载并创建了该Servlet的实例对象,Servlet直接进行第4步,否则,执行第2步。
- 装载并创建Servlet的一个实例对象。
- 调用Servlet实例对象的init()方法,以便执行Servlet的一些初始化工作。
- 创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletReponse对象,然后调用Servlet的service()方法并将请求和响应消息作为参数传递进去。
- 在一个Web应用程序被停止或重新启动之前,Servlet引擎将卸载其中运行的Servlet。在卸载Servlet之前,Servlet引擎将调用Servlet的destory()方法,以便在这个方法中执行Servlet的一些清尾工作。
注意:
- 上面的2和3步过程一般是在Web服务器重新启动后,针对Servlet的第一次访问完成的。也可以让Web应用程序启动时就完成这两步过程。在Web.xml文件中定义的<servlet>元素中可以嵌套一个名为<load-on-startup>的子元素,这个子元素用于指定Servlet被装载的时机和顺序。如果这个子元素的内容被设置为0或一个正整数,指定该Servlet应该在Web应用程序启动时就被实例化和调用它的init()方法,且这个数字越小,Servlet被装载的时间也越早,相同数字的Servlet之间的装载顺序由Servlet引擎决定。如果被设置成一个负整数,则由Servlet引擎决定什么时候装载Servlet.
- 在Servlet整个生命周期中,Servlet只被初始化一次,而对一个Servlet的每次访问请求都导致Servlet引擎调用一次Servlet的service方法。所以,在Servlet整个生命周期内,init方法只被调用一次,service方法可以被调用多次。
- 如果对某个被装载的Servlet进行了修改,都需要重新启动Tomcat才能看到被修改的内容。因此Tomcat也提供是否自动重新装载被修改的Servlet的配置选项。在<Tomcat安装主目录>/conf/server.xml文件中,可以使用<Context>元素来设置每个独立的Web应用程序的配置信息,<Context>元素中有一个reloadable属性,当它的设置值为true时,Tomcat将监视该Web应用程序的/Web-INF/classes和/Web-INF/lib目录下的类是否发生了改变,然后自动重新装载那些发生了改变的类,并在Tomcat所运行的命令行窗口显示出重新装载的有关提示信息。
Servlet的生命周期
接口中定义的init,service,destory三个方法是Servlet生命周期的方法,Servlet引擎会根据下面的规则来调用这三个方法:
- init(),当Servlet第一次被请求时,Servlet容器就会开始调用这个方法来初始化一个Servlet对象出来。但是这个方法在后续的请求中不会再被Servlet引擎调用。可以利用这个方法来执行相应的初始化工作。调用这个方法时,Servlet引擎会传入一个ServletConfig对象进而对Servlet对象进行初始化。
- service(),每当请求Servlet时,Servlet容器就会调用这个方法。第一个请求时,Servlet引擎会先调用init()方法初始化一个Servlet对象出来,然后会调用它的service方法进行工作。但是在后续的请求中,Servlet引擎就只会调用service方法。
- destory(),当要销毁Servlet时,就会调用这个方法。在卸载应用程序或关闭Servlet容器时,就会发生该情况,一般在这个方法中写一些清除代码。
ServletConfig接口
Servlet程序是发布到Web应用程序中运行的,此Web应用程序就称为Servlet容器。Servlet是在Servlet容器内运行的程序,它在有些情况下可能需要访问Servlet容器或借助Servlet容器访问外部资源,所以Servlet引擎需要将表示Servlet容器的对象传递给Servlet。在Web.xml文件中为某个Servlet设置的友好名称和初始化参数等信息都需要传给Servlet。Servlet引擎将代表Servlet容器的对象和Servlet配置参数信息一并封装到一个称为ServletConfig对象中,并在初始化Servlet实例对象时传递给Servlet。ServletConfig接口则用于定义ServletConfig对象需要对外提供的方法,以便可以在Servlet程序中可以调用这些方法来获取信息。
其中的几个方法如下所示:
-
getInitParameterNames 用于返回一个Enumeration集合对象,该集合对象中包含在Web.xml文件中为当前Servelt设置的所有初始化参数的名称。
-
getInitParameter 用于返回在Web.xml文件中为Servlet所设置的某个名称的初始化参数值,如果指定名称的初始化参数不存在,则返回值为NULL。
-
getServeltName 用于返回Servlet在Web.xml文件中的注册名称。
-
getServeltContext 用于返回ServletConfig对象中所包含的ServletContext对象的应用(在Servlet程序中,每个Web应用程序-Servlet容器都用一个各自的ServletContext对象来表示)。
ServletContext接口
每个Web应用程序都是一个独立的Servlet容器,每个Web应用程序分别用一个ServletContext对象来表示。ServletContext接口定义了ServletContext对象需要对外提供的方法,Servlet程序通过这些方法与Servlet容器进行通信。Servlet引擎为每个Web应用程序创建一个对应的ServletContext对象,这个对象被包含在ServletConfig对象中,调用ServletConfig.getServletContext方法可以返回ServletContext对象的引用。在容器初始化Servlet对象时,ServletContext对象随着ServletConfig对象提供给Servlet。
1.获得Web应用程序的初始化参数
在server.xml文件中为某个Web应用程序设置初始化参数,需要在该Web应用程序所对应的<Context>元素中添加<Parameter>子元素(这个子元素中有name,value,override等属性,override属性用于指定在Web应用程序的Web.xml文件中设置的同名初始化参数是否覆盖这里的设置)。
在Web的应用程序的Web.xml文件中设置初始化参数,需要在根元素<Web-app>中添加<context-param>子元素。
ServletContext通过getInitParameter方法获得这些初始化参数。
2.记录日志
ServletContext接口中定义了两个重载log方法来记录日志,定义如下:
public void log(java.lang.String msg)
public void log(java.lang.String message,java.lang.Throwable t)
3.application域范围的属性
由于一个Web应用程序中的所有Servelt都共享同一个ServletConext对象,所以ServletContext对象被称为application对象(Web应用程序对象)application对象(ServletContext)内部中有一个哈希表集合对象,存储进application对象的哈希表集合对象中的每队关键字/值被称为application对象的属性。由于一个Web应用程序中的所有Servlet都共享同一个application对象,所以application对象的属性可以被Wen应用程序内所有的Servlet程序访问,存储在application对象中的属性也被称为application域范围属性。application域范围的属性可以被当做该Web应用范围内的全局变量使用。ServletContext接口定义了4个分别用于增加,删除,访问application域范围属性的方法。
- getAttributeNames 用于返回一个Enumeration集合对象,包含了application对象中的所有属性名称。
- getAttribute 用于返回某个属性的值
- removeAttribute 用于删除某个属性
- setAttribute 用于增加一个属性
4.访问资源文件
ServletContext接口中还定义了一些用于访问Web应用程序的内部资源文件的方法,这些资源文件是指Web应用程序内部各种形式的文件,包括Web-INF目录中不能被外界访问的文件。
- getResourcePaths 返回一个java.util.Set集合对象,该Set集合对象中包含某个资源目录中所有子目录和文件的路径名称,每个路径名称都以相对整个Web应用程序的根目录的形式表示,即都用'/'开头,其中每个目录路径的最后都有'/'结尾。
- getResource 返回映射到某个资源的URL对象,传递给getResource方法的参数必须是一个以“/”开头的资源路径名。
- getResourceAsStream 返回连接到某个资源的InputStream对象,这个方法实际上打开getResource方法返回URL对象的输入流,并返回这个输入流对象供程序直接读取数据,参数传递规则与getResource方法一样。
5.获取虚拟路径所映射的本地路径
- getRealPath方法,用于返回某个虚拟路径所映射的本地文件系统路径。如果传递给getRealPath方法路径字符串以“/”作为起始字符,这个代表了当前Web应用程序的根目录。
6.Web应用程序之间的访问
ServletContext接口中定义了一个getContext方法来获得某个URL所对应的ServletContext对象,传递给getContext方法的路径字符串必须以“/”作为起始字符,这个代表了整个Web服务器的根目录,而不是某个Web应用程序的根目录,这说明在一个Web应用程序中可以获得其他Web应用程序的ServletContext对象。如果要让某个Web应用程序内部能够使用Servlet.getContet方法返回代表其他Web应用程序的ServletContext对象,必须将<Tomcat安装主目录>/conf/server.xml文件中与该Web应用程序对应的<Context>元素的crossContext属性设置为true。
7.其他方法
- getMajorVersion 返回Servelt容器所支持的Servelt API的主要版本号。
- getMinorVersion 返回Servelt容器所支持的Servelt API的次版本号。
- getMimeType 返回某个文件名对应的MIME类型。
- getServerInfo 返回Servelt容器的名称和版本信息。
ServletRequest接口
Servlet容器对于接受到的每一个Http请求,都会创建一个ServletRequest对象,并把这个对象传递给Servlet的Sevice( )方法。其中,ServletRequest对象内封装了关于这个请求的许多详细信息。
public interface ServletRequest {
int getContentLength();//返回请求主体的字节数
String getContentType();//返回主体的MIME类型
String getParameter(String var1);//返回请求参数的值
}
ServletResponse接口
javax.Servlet.ServletResponse接口表示一个Servlet响应,在调用Servlet的Service( )方法前,Servlet容器会先创建一个ServletResponse对象,并把它作为第二个参数传给Service( )方法。ServletResponse隐藏了向浏览器发送响应的复杂过程。
public interface ServletResponse {
String getCharacterEncoding();
String getContentType();
ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;
void setCharacterEncoding(String var1);
void setContentLength(int var1);
void setContentType(String var1);
void setBufferSize(int var1);
int getBufferSize();
void flushBuffer() throws IOException;
void resetBuffer();
boolean isCommitted();
void reset();
void setLocale(Locale var1);
Locale getLocale();
}
GenericServlet和HttpServlet类
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
private static final String LSTRING_FILE = "javax.servlet.LocalStrings";
private static ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.LocalStrings");
private transient ServletConfig config;
public GenericServlet() {
}
public void destroy() {
}
public String getInitParameter(String name) {
ServletConfig sc = this.getServletConfig();
if (sc == null) {
throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
} else {
return sc.getInitParameter(name);
}
}
public Enumeration<String> getInitParameterNames() {
ServletConfig sc = this.getServletConfig();
if (sc == null) {
throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
} else {
return sc.getInitParameterNames();
}
}
public ServletConfig getServletConfig() {
return this.config;
}
public ServletContext getServletContext() {
ServletConfig sc = this.getServletConfig();
if (sc == null) {
throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
} else {
return sc.getServletContext();
}
}
public String getServletInfo() {
return "";
}
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
}
public void log(String msg) {
this.getServletContext().log(this.getServletName() + ": " + msg);
}
public void log(String message, Throwable t) {
this.getServletContext().log(this.getServletName() + ": " + message, t);
}
public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
public String getServletName() {
ServletConfig sc = this.getServletConfig();
if (sc == null) {
throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
} else {
return sc.getServletName();
}
}
}
GenericServlet是一个实现了Servelt的基本特征和功能的基类,其完整名称为javax.servlet.GenericServlet。
HttpServlet是GenericServlet的子类,其完整名称为javax.servlet.http.HttpServlet,它提供处理HTTP协议的基本架构。
1.init方法
init方法是在Servlet对象呗创建后,再由Servlet引擎调用的方法,init方法的调用位于构造方法之后。Servlet引擎在调用init方法时,会传递一个包含Servlet配置和运行环境信息的ServletConfig对象。
public void init(ServeltConfig config) throw ServeltException
GenericServlet还定义了一个无参的init方法:
public void init() throw ServeltException
这个无参的init()方法没有编写任何程序语句,是一个空函数。GenericServlet实现的init(ServletConfig config)方法中调用这个无参数的init()方法,所以,对于继承GenericServlet类的Servelt程序,也可以覆盖这个无参数的init()方法来编写初始化代码,并且覆盖这个无参数的init()方法还有一个好处,省去覆盖了initServletConfig config)方法总是要编写super.init(config)语句的麻烦。
由于子类覆盖GenericServlet的init(ServletConfig config)方法后,init(ServletConfig config)方法不再被调用,为了不破坏GenericServlet的其他方法的功能,GenericServlet子孙类中的init(ServletConfig config)方法首先必须完成GenericServlet类init(ServletConfig config)方法的功能,即应该使用super.init(config)语句调用GenericServlet子孙类中的init(ServletConfig config)方法,然后才能编写自己的扩展初始化代码。
public void init(ServeltConfig config) throws ServeltException
{
this.config=config;
init();
}
如果GenericServlet的子孙类要增加一些额外的初始化功能,只需要覆盖无参数的init()方法。这样,Servlet容器在初始化由GenericServlet派生出的子类Servlet程序时,调用的仍然是GenericServlet的有参数的init()方法,但是由于该方法最后调用了无参数的init()方法,根据面向对象的多态性,这里调用的无参数的init()方法不再是GenericServlet自身的无参数的init()方法,而实际子类中的init()方法。
2.service方法
每当针对某个Servlet的访问请求到达时,Servlet引擎就会调用该Servlet实例对象的service方法来进行响应。Servlet接口中定义的service方法的语法格式为:
public void service(ServeltRequest req,ServeltResponse res)
throws ServeltException,java.io.IOException
GenericServlet类没有对这个方法进行实现,HttpServlet类实现这个方法。由于Servlet接口定义的service方法接受的参数类型分别是ServletRequest和ServletResponse,对于采用HTTP协议的访问请求,如果要在这个service方法内部使用HTTP消息的特有功能,也就是要调用HttpServletRequest和HttpServletResponse类中定义的方法时,还需要进行转换。
为了简化转换的过程,HttpServlet类实现的service方法内部调用另一个重载形式service方法,重载的service方法定义的语法是:
protected void service(HttpServeltRequest req,HttpServeltResponse resp)
throws ServeltException,java.io.IOException
在HttpServlet所实现的Servelt接口定义的service方法的内部,将请求和响应对象的类型转换成HttpServletRequest和HttpServletResponse类型后。再将它们作为参数传递给这个重载的service方法。如下所示:
public void service(ServeltRequest req,ServeltResponse res)
throws ServeltException,java.io.IOException
{
HttpServeltRequest request=(HttpServeltRequest) req;
HttpServeltResponse response=(HttpServeltResponse) res;
service(request,response);
}
由于Servlet引擎与Servlet程序是按Servlet接口中约定的方法进行通信。对于Servlet程序中的多个重载形式的service方法,Servlet引擎只会调用接口中定义的方法,其他形式的方法相当于Servlet程序内部定义的普通方法,Servlet引擎根本不知道它们的存在。
3.destroy
destroy方法在Web容器卸载Servlet之前被调用,在Servlet的生命周期中也只执行一次。可以通过覆盖destroy方法来完成与init方法相反的功能,释放该Servlet打开的资源。GenericServlet类实现的destroy方法已经满足了通常的需要,子类Servlet一般不必覆盖这个方法。
4.getServletConfig
getServletConfig方法用于返回Servelt引擎通过init方法传递进来的那个ServletConfig对象的引用。
5.getServletnfo
用于返回Servlet的描述信息。如果有必要,可以对这个方法进行覆盖,以便返回Servlet的作者,版本和版权等信息。
6.doXXX(HttpServlet)
客户端可以用HTTP协议中规定的各种请求方式来访问Servlet,对于不同的请求方式,Servlet可能需要采用不同的方式进行处理。不管客户端以哪种请求方式访问Servlet,Servlet引擎都将调用Servelt的service方法,service方法是处理所有请求方式的总入口。
HttpServlet类为每一种HTTP请求方式定义了一个对应的doXXX方法,例如,与GET请求方式对应的是doGet方法。HttpServlet中重载的service方法根据客户端的请求方式,分别调用与之对应的doXXX方法来完成具体的处理和响应细节,并将它接受的两个参数传递给该doXXX方法。需要实现具体的服务逻辑,不再需要覆盖service方法了,只需要覆盖doGet或者doPost就好了。
7.getLastModified
响应消息中的Last-Modified头字段可用于指定响应内容的最后更新时间,当客户端缓存此文档内容,它在以后的请求消息将根据Last-Modified头字段指定的时间来生成If-Modified-Since请求头字段,以指出缓存文档的最后更新时间。只有文档的修改时间比If-Modified-Since请求头字段的时间新时,服务器才会返回文档内容。如果自从If-Modified-Since指定的时间以来,网页内容没有发生修改,服务器将返回一个304状态码来表示浏览器的缓存版本是最新的,而不会向浏览器返回文档内容,浏览器继续使用以前的缓存的内容。
HttpServlet类为If-Modified-Since请求头和Last-Modified头字段的这种应用提供了处理机制。在HttpServlet类中定义了一个getLastModified方法。这个方法的返回值可以影响浏览器如何处理和利用缓存。该方法的完整语法定义如下:
protected long getLastModified(HttpServeltRequest req)
其中的返回值代表Servelt当前输出的响应内容的修改时间。getLastModified方法是一个回调方法,由HttpServlet类的servie方法调用,可以根据这个返回值在响应消息中自动生成Last-Modified头字段。HttpServlet方法定义的getModified方法总是返回一个负数,子类可以对这个方法进行覆盖。
当继承HttpServlet类的Servlet程序接收到一个GET方法的访问请求时,HttpServlet中重载的service方法在调用doGet方法之前,它还将先调用getLastModified方法,并根据返回值决定是否调用doGet方法和响应消息中是否生成Last-Modified头字段,具体规则如下:
- getLastModified方法返回一个负数时,不管请求消息中的情况怎样,service方法都将直接调用doGet方法来生成响应内容。
- 返回一个正数时,且请求消息中没有包含If-Modified-Since请求头(这往往出现在对某个资源的第一次访问时),或者请求消息中包含的If-Modified-Since请求头中的时间值比getLastModified方法返回值的时间值旧时,service方法将根据getLastModified方法的返回值生成一个Last-Modified头字段,然后调用doGet方法生成响应内容。
- 返回一个正数时,请求消息中包含的If-Modified-Since请求头中的时间值比getLastModified方法返回值的时间值新或者相同时,service方法将不会调用doGet方法,而是向浏览器返回一个304状态码来通知浏览器使用以前缓存的内容。
HttpServletResponses
Web服务器回送给Web客户端的HTTP响应消息分为三个部分:状态行,响应消息头,消息正文。ServletAPI中定义的ServletResponse接口类用于创建响应消息,Servlet程序通过调用ServletResponse对象的方法可以向客户端回送基本的响应消息。HttpServletResponse是专用于Http协议的ServletResponse子接口,它用于封装HTTP响应消息,允许操控HTTP协议相关数据,包括响应头和状态码,支持Cookies和session跟踪,HtteServletResponse也定义一系列用于描述HTTP状态码的常量。
HTTP响应消息的响应状态行可以分为三个部分:HTTP版本,状态代码和一条相关的提示信息。
1.产生响应状态行
1.1 setStatus
setStatus方法用于设置HTTP响应消息的状态码,并生成响应状态行(因为响应状态行中的提示信息直接与状态代码相关,且HTTP版本由服务器确定,只需要设置响应状态代码就足够了)。
void setStatus(int var1);//设置响应行的状态码
setStatus方法接收一个int类型的参数,这个参数就是要设置的响应状态码,正常情况下的想要吗为200。通常情况下的Servelt不需要调用HttpServeltResponse的setStatus方法来指定状态代码和产生响应状态行。只有在HTTP响应消息中使用一个特殊的状态码,才需要调用setStatus方法。
由于HTTP响应消息是由状态行,一个或多个响应头,实体内容顺序组成的。所以,如果Servlet没有使用或不支持输出缓冲区,那么就一定要在调用其他可产生客户端输出内容的方法调用setStatus方法。即使Servlet使用了输出缓冲区,也必须在输出缓冲区的内容尚未被实际发送到客户端之前调用setStatus方法。
1.2 sendError
sendError方法用于发送表示错误信息的状态码到客户端,并清除缓冲区的内容。HttpServletResponse中提供两个重载的sendError方法,定义如下:
//发送错误信息的状态码
public void sendError(int code) throws java.io.IOException
//发送错误信息的状态码和用于提示说明的文本信息(出现在发送给客户端的正文内容)
public void sendError(int code,String message) throws java.io.IOException
2.构建响应消息头
2.1 addHeader和setHeader
这两个方法都可用于设置HTTP响应消息的各种头字段,语法定义如下:
//第一个参数为响应头的字段名,第二个参数为字段取值
public void addHeader(java.lang.String name,java.lang.String value)
public void setHeader(java.lang.String name,java.lang.String value)
这两个方法都是增加一个新的字段。其中,如果已经设置过同名的字段名,setHeader方法用新的设置值取代原来的设置值,而addHeader方法则是增加一个同名的响应头。
2.2 addIntHeader和setIntHeader
HttpServletResponse提供了两个专门用于设置包含整数值的响应头的方法,定义语法为:
//第一个参数为响应头的字段名,第二个参数为字段取值
public void addIntHeader(java.lang.String name,int value)
public void setIntHeader(java.lang.String name,int value)
这两个方法将int类型的值设置给一个响应头之前必须转换为String类型。
2.3 addDateHeader和setDateHeader
HttpServletResponse提供了两个专门用于设置包含日期值得响应头的方法,定义语法为:
public void addDateHeader(java.lang.String name,long date)
public void setDateHeader(java.lang.String name,long date)
2.4 setContentLength
setContentLength方法用于设置响应消息的实体内容的大小,单位为字节。因为浏览器与Web服务器之间使用持久的HTTP链接,如果Web服务器没有采用chunker传输编码方式,那么它必须在每个应答中发送一个Content-Length的响应头来表示各个实体内容的长度,以便客户端能分辨出上一个响应内容的结束位置。
2.5 setContentType
用于设置Servelt输出内容的MIME类型。Servlet引擎默认设置的输出内容的类型为"text/plain",即普通文本类型,而网页文档的类型通常是“text/html”,所以需要显式调用setContentType方法进行制定。在MIME类型后面还可以指定响应内容所使用的字符集类型。如果没有指定字符集类型,且使用ServletResponse.getWriter方法返回的PrintWriter对象输出文本内容时,会默认增加ISO8859-1字符集类型。
2.6 setCharacterEncoding
用于设置输出内容的MIME声明中的字符集编码,对于HTTP协议来说,就是设置Content-Type头字段的字符集编码部分。如果没有设置Content-Type头字段,这个方法设置的字符集编码不会出现在HTTP消息的响应头,但是它的设置结果仍然决定了ServletResponse.getWriter方法返回的PrintWriter对象输出文本内容所采用的字符集编码。这个方法的优先权高,它的设置结果会覆盖setContentType和setLocale方法设置的字符集编码
2.7 setLocale
用于设置响应消息的本地化信息,对于HTTP来说,它将设置Content-Language响应头字段和Content-Type头字段的字符集编码部分。果没有设置Content-Type头字段,这个方法设置的字符集编码不会出现在HTTP消息的响应头,但是它的设置结果仍然决定了ServletResponse.getWriter方法返回的PrintWriter对象输出文本内容所采用的字符集编码。如果调用了setCharacterEncoding和setContentType方法指定响应消息的字符集编码,setLocale方法将不再具有指定字符集编码的功能,语法定义如下:
public void setLocale(java.util.Locale loc)
Locale对象只包含了语言和国家地区信息,没有包含字符集编码信息。Locale信息与字符集编码有一种映射关系,在部署描述符文件Web.xml中可以通过<locale-encoding-mapping-list>元素来设置各种Locale与字符集编码之间的映射关系。
2.8 containsHeader
用于检查某个名称的头字段是否已经被设置。
2.9 禁止浏览器缓存当前文档内容
有三个响应头字段都可以禁止浏览器缓存当前页面,代码如下:
response.setDateHeader("Expires",0);
response.setHeader("Cache-Control","no-cache");
response.setHeader("Pragma","no-cache");
3.创建响应正文
ServletResponse是Servelt程序与Servlet引擎进行通信的接口,Servlet程序通过ServletResponse对象将响应正文传递给Servlet引擎,再由引擎将响应正文输出到客户端。如果模块之间传递大量的数据,最好以输入或输出流作为两个模块传递数据的接口方式。
3.1 getOutputStream和getWriter
getOutputStream方法用于返回Servelt引擎创建的字节输出流对象。getWriter方法用于返回Servelt引擎创建的字符输出流对象。这两个方法互斥,调用了一个方法就不可以调用另一个方法。
getOutputStream方法返回的字节输出流对象的类型为ServeltOutpputStream,ServletOutpputStream是OutputStream的子类,它可以直接输出字节数组中的二进制数据。
getWriter方法将Servelt引擎的数据缓冲区包装成PrintWriter类型的字符输出流对象后返回,PrintWriter对象可以直接输出字符文本内容。
Servlet的service方法结束后,Servelt引擎将检查这两个方法返回的输出流对象是否已经调用过close方法。如果没有,调用close方法关闭输出流对象。
可以多次调用getWriter方法,每次调用的getWriter方法返回的PrintWriter对象的引用变量指向的是同一个PrintWriter对象,按照调用顺序写入同一输出流。同样,getOutStream返回的也将是同一个ServletOutputStream对象的引用。
3.2 输出缓冲区
ServletResponse定义了与缓冲区相关的方法:
- setBufferSize 设置期望的输出缓冲区大小。
- getBufferSize 返回Servelt引擎实际使用的缓冲区的大小。
- flushBuffer 将输出缓冲区内容强制输出到客户端。
- reset 用于清空缓冲区的内容,以及设置的响应状态码和各个响应头。如果当前响应已经向客户端输出过部分内容,这个方法将会抛出IllegalStateException异常。
- isCommitted 判断是否已经提交部分响应内容到客户端,如果已经提交,返回true。
4. 请求重定向和请求转发
4.1 RequestDispatcher接口-请求转发
ServletAPI 定义了一个RequestDispatcher接口,它定义了RequestDispatcher实例对象的方法。RequestDispatcher实例对象是由Servlet引擎创建的,用于包装一个要被其他资源调用的资源,并可以通过其中的方法将客户端的请求转发给所包装的资源。
RequestDispatcher接口定义了两个方法:forward方法和include方法,它们分别用于将请求转发到RequestDispatcher对象封装的资源和RequestDispatcher对象封装的资源作为当前响应的内容的一部分包含进来。接收的参数必须是传递给当前Servlet的service方法的ServletResponse和ServletRequest对象,或者是对它们进行包装的ServletResponseWrapper和ServletRequestWrapper对象。
ServletContext接口中定义了两个获得RequestDispatcher对象的方法:
- getRequestDispatcher 返回包装了某个路径所指定资源的RequestDispatcher对象,传递给该方法的路径必须以“/”开头。Web-INF目录中的内容对RequestDispatcher对象是可见。
- getNameDispatcher 返回了某个Servelt或JSP文件的RequestDispatcher对象。传递给该方法的参数是在Web应用程序部署描述符中为Servelt或JSP文件指定的友好名称。
在ServletRequest接口中也定义了getRequestDispatcher方法用于获得RequestDispatcher对象,区别在于:传递给该方法的参数除了是以“/”开头的路径字符串,也可以采用非“/"开头的相对字符串。
RequestDispatcher对象只能包装当前Web应用程序中的资源,所以,forward和include方法只能在同一个Web应用程序内的资源之间转发请求和资源包含。
4.1.1 include-实现资源包含
RequestDispatcher.include方法用于将RequestDispatcher对象封装的资源作为当前响应内容的一部分包含进来,从而实现可编程的服务器包含功能。被包含的Servlet程序不能改变响应消息的状态码和响应头,如果存在这样的语句,执行结果将会被忽略。在调用RequestDispatcher.include方法时,Servlet引擎不会去调整HttpServletRequest对象中的信息,仍然保持初始的URL路径和参数信息,也就是在被调用者程序检索当前访问路径时,得到的是调用程序的URL路径。
4.1.2 forward-实现请求转发
forward方法用于将请求转发到RequestDispatcher对象封装的资源。使用forward方法时,需要注意下面问题:
-
调用RequestDispatcher.forward方法时,Sevelt容器将根据目标资源路径对HttpServletRequest对象中的请求路径和参数信息进行调整。
-
如果在调用forward方法之前,在Servelt程序写入的部分内容已经被真正的传送到客户端,将抛出IllegalStateException。
-
如果在调用forward方法之前向Servlet引擎的缓冲区写入内容,只要写入到缓冲区的内容没有被真正输出到客户端,forward方法就可以被正常执行,原来写入到输出缓冲区的内容将被情况。在调用forward方法之后,还执行写入操作,这些结果都将被忽略。
-
调用者和被调用者的响应状态码和响应头不会被忽略。
-
当调用者和被调用者的URL不属于同一个目录时。当被调用者输出的内容包含相对URL的访问路径时,原来相对被调用者的URL将变成相对于调用者的URL。
4.2 HttpServletResponse.sendRedirect-请求重定向
sendRedirect方法用于生成302响应码和Location响应头,从而通知客户端去重新访问Location响应头中指定的URL。定义语法如下:
//location 参数指定了重定向的URL,可以使用相对URL(引擎会自动将相对URL转换成绝对URL,再生成Location字段)
public void sendRedirect(java.lang.String location) throws java.IO.IOException
使用下面两条语句也可以完成sendRedirect语句完成的功能:
response.setStatus(response.SC_MOVED_TEMPORARILY);
response.setHeader("Location",url); //这个URL必须是绝对URL
sendRedirect方法不仅可以重定向到当前应用程序的其他资源,还可以重定向到同一个站点的其他应用程序的资源,甚至是使用绝对URL重定向到其他站点的资源。
4.3 请求重定向和请求转发的比较
- RequestDispatcher.forward方法只能将请求转发给同一个Web应用程序的组件;而HttpServeltResponse.sendRedirect方法不仅可以重定向到当前应用程序的其他资源,还可以重定向到同一个站点的其他应用程序的资源,甚至是使用绝对URL重定向到其他站点的资源。如果传递给HttpServletResponse.sendRedirect方法的相对URL以“/”开头,是相对整个Web站点的根目录。
- 调用HttpServletResponse.sendRedirect方法重定向的访问过程结束后,浏览器地址栏显式的URL会发生改变,由初始的URL地址变成重定向的URL。调用RequestDispatcher.forward方法的请求转发过程结束后,地址不变。
- HttpServletResponse.sendRedirect方法对浏览器的请求直接做出响应,响应的结果就是告诉浏览器区重新发出对另外一个URL的访问请求。RequestDispatcher.forward方法在服务器内部将请求转发给另一个资源,浏览器只知道发出了请求并得到了响应结果,并不知道这个内部行为。
- RequestDispatcher.forward方法的调用者和被调用者之间共享request对象和response对象,它们属于同一个访问请求进而响应过程。而HttpServletResponse.sendRedirect方法调用者和被调用者使用各自的request和response对象,属于两个独立的访问请求和响应过程。
- 无论是HttpServletResponse.sendRedirect方法,还是RequestDispatcher.forward方法,在调用它们之前,都不能有内容被实际输出到客户端,如果缓冲区中已经有了一些内容,这些内容被从缓冲区清除。
HttpServletRequest
Web客户端发送给Web服务器的HTTP请求消息可以分为三个部分:请求行,请求消息头,消息正文。消息正文只在POST,PUT和DELETE等方式发出的请求消息中才出现。ServletAPI中定义了一个ServletRequest接口类作为获取客户端请求消息的基本接口。HttpServletRequest是专用于HTTP协议的ServeltRequest子接口,用于封装HTTP请求消息,增加了获取HTTP协议专有的头信息的方法,支持Cookie和session跟踪,以及获取HTTP请求消息参数的功能。
1.获取请求行相关信息
HTTP请求消息的请求行可以分为三个部分:请求方式,资源路径和HTTP协议版本
- getMethod 用于返回HTTP请求消息中的请求方式(如GET,POST等),也就是请求行第一部分的内容,
- getRequestURL 返回请求行中的资源名部分,位于URL的主机和端口之后,参数部分之前的那部分内容。
- getQueryString 返回请求行中的参数部分,也就是资源路径后面问号以后的所有内容。返回结果没有被进行URL解码,而是保持其在HTTP消息的请求行中的原始面貌。
- getProtocol 返回请求行的中的协议名和版本。
- getContextPath 返回请求URL所属于的Web应用程序的路径,这个路径以“/”开头,表示相对于整个Web站点的根目录。
- getPathInfo 返回请求URL中的额外路径信息。额外路径信息是请求URL中位于Servelt的路径之后和查询参数之前的内容,以“/”开头。额外路径可用于向Servelt传递参数,但它传递的信息通常是指Web服务器上的其他资源名。
- getPathTranslated 返回URL中额外路径信息对应的资源的真实路径
- getServeltPath 返回Servelt的名称或Servelt所映射的路径。
2.获取网络连接信息
- getRemoteAddr 返回发出请求的客户机的IP地址,其格式为“192.168.0.0”这种形式的字符文本。
- getRemoteHost 返回发出请求的客户机的完整主机名。如果Servelt引擎不能解析出完整主机名,就返回客户机的IP地址。
- getRemotePort 返回发出请求的客户机所使用的的网络接口的端口号。
- getLocalAddr 返回Web服务器上接收当前请求的网络接口的IP地址。
- getLocalName 返回Web服务器上接收当前请求的网络接口的IP地址所对应的主机名。
- getLocalPort 返回Web服务器上接收当前请求的网络接口的端口号。
- getServerName 返回当前请求所指向的主机名。
- getServerPort 返回当前请求所连接的服务器端口号。
- getScheme 返回请求的协议名。
- getRequestURL 返回客户端发出请求的完整URL,包括协议,服务器名,端口号,资源路径等信息,但不包括后面查询参数的部分。
3. 获取请求头信息
3.1 getHeader
getHeader方法用于返回一个指定名称的头字段的值,定义语法如下:
public java.lang.String getHeader(java.lang.String name)
如果请求消息中没有包含指定名称的头字段,getHeader返回null。含有多个指定名称的头字段,返回其中的第一个头字段的值。
3.2 getHeaders
返回一个Enumeration集合对象,该集合对象由请求消息中出现的某个指定名称的所有头字段值组成的。
public java.util.Enumeration getHeader(java.lang.String name)
3.3 getHeaderNames
返回一个包含请求消息中的所有头字段名的Enumeration对象。
public java.util.Enumeration getHeaderNames()
3.4 getIntHeader
用于获取一个指定名称的头字段值并将其转换成int类型返回,如果指定名称的头字段不存在,返回值为-1。如果获得头字段值不能转换成整数,抛出NumberFormatException。
public int getIntHeader(java.lang.String name)
3.5 getDateHeader
用于获取一个指定名称的头字段得值并将其按照GMT时间格式转换成一个代表日期/时间的长整数返回。
public long getDateHeader(java.lang.String name)
3.6 getContentType
直接返回Content-Type头字段的值,结果为String类型。
3.7 getContentLength
直接返回Content-Length头字段的值,结果为int类型。
3.8 getCharacterEncoding
返回请求消息的实体部分的字符集编码,通常从Content-Type头字段中进行提取。
4.获取浏览器传递给Web服务器的参数信息
HttpServeltRequest(ServeltRequest)对象最基本和最广泛应用就是获取浏览器传递给服务器的参数信息,这些参数信息可以是HTTP请求消息中请求行中的URL地址后的附加信息,又可以是POST方式下“application/x-www-form-urlencoded”编码格式的实体内容。
4.1 使用GET方式传递参数
如果使用GET请求方式给Web服务器传递参数,采取的方式就是讲这些参数附加在URL地址后,URL地址与后面的参数使用?分隔。使用GET方式传递的数据量是有限制的,一般限制在1KB之下。
4.2 使用POST方式传递参数
当使用POST方式来提交FORM表单中的数据时,浏览器将各个表单字段元素及其数据作为HTTP消息的实体内容发送给Web服务器。对于采用POST请求方式的HTTP请求消息,HttpServletRequest接口中定义的getParameter等方法只能从“application/x-www-form-urlencoded”编码类型的实体内容中将表单字段元素当参数提取出来。
根据HTML标准,如果处理表单的服务器程序不会改变Web服务器上存储的数据,采用GET方式。反之采用POST方式。
5.获取请求参数
5.1 getParameter
返回某个指定名称的参数的值。
public java.lang.String getParameter(java.lang.String name)
如果请求消息中没有包含指定名称的头字段,getParameter返回null。含有多个指定名称的头字段,返回其中的第一个头字段的值。如果指定名称的参数存在但没有设置值,则返回一个空串。
5.2 getParameterValues
public java.lang.String[] getParameterValues(java.lang.String name)
用于获取某个指定名称的所有参数的值,并以一个String数组的形式返回这些参数值。
5.3 getParameterNames
public java.util.Enumeration getParameterNames()
返回一个包含请求消息中所有参数名的Enumeration对象。
5.4 getParameterMap
public java.util.Map getParameterMap()
用于将请求消息中所有参数名和值装入一个Map对象中返回。
6. 获取请求消息的实体内容
6.1 getInputStream和getReader
getInputStream方法用于返回一个代表实体内容的输入流对象,其类型是java.servlet.ServletnputStream。使用这个输入流对象可以读取HTTP请求消息中的实体内容,如果实体内容中包含二进制数据,只能使用getInputStream方法返回的输入流来读取。
getReader方法用于返回一个代表实体内容的BufferReader对象,返回的BufferReader对象将实体内容中的字节数据按照请求消息中指定的字符集编码转换成文本字符。在调用getReader之前,可以调用ServletRequest的setCharacterEncoding方法指定其返回BufferReader对象所使用的字符集编码。
调用了这两个方法中的任何一个方法后,就不能再调用另一个方法。
7. 利用请求域属性传递信息
ServletRequest接口的实现类通常是采用一个HashMap来存储对象的。setAttribute方法就是向这个HashMap对象中增加与某个名称绑定的对象,getAttribute方法则是根据这个名称从HashMap对象中检索对象。这种存储在ServletRequest对象中的对象称之为请求域对象,属于同一个请求的多个处理模块之间可以通过请求域来传递对象数据。
7.1 setAttribute
public void setAttribute(java.lang.String name,java.lang.Object o)
用于将一个对象与一个名称关联后存储进ServeltRequest对象中。
7.2 getAttribute
public void getAttribute(java.lang.String name)
用于从ServeltRequest对象中返回指定名称的属性对象。
7.3 removeAttribute
public void removeAttribute(java.lang.String name)
用于从ServeltRequest对象中删除指定名称的属性
7.4 getAttributeNames
public java.util.Enumeration getAttributeNames()
用于返回一个包含Servelt对象中所有属性名的Enumeration对象。
8.请求参数的中文读取问题
HTTP协议规定浏览器向Web服务器传递的参数信息不能出现某些特殊字符,而必须对这些字符进行URL编码后再传送。Web服务器端创徐接收到客户端传递的整个参数信息,首先从中分离出每个参数的名称和值部分,然后对单个名称和值进行URL解码,然后将URL解码得到的字节数组按照某种 字符集编码转换成Unicode字符串。
对Java中的某个字符串进行URL编码,其实就是对它的某种字符集编码(非Unicode码)数据进行URL编码。
setCharacterEncoding方法用于覆盖请求消息中实体内容的字符集编码名称的设置。getParameter和getReader方法将读取道德实体内容从字节数组形态转换成字符串返回时,都有参照请求消息中实体内容的字符集编码名称。
8.1 getParameter方法的中文问题
ServletRequest对象的getParameter等方法除了可以读取HTTP请求消息的请求行中的URL地址后的附加信息外,还可以读取POST方式下“application/x-www-form-urlencoded”编码格式的实体内容。浏览器传递这些供ServletRequest对象的getParameter等方法读取的参数信息外,必须对其中的包含中文字符在内的特殊字符进行URL解码,这些参数是以哪种字符集进行URL编码,就以哪种字符集进行URL解码。
-
对于HTTP请求消息的请求行中的URL地址后的参数,getParameter等方法进行URL解码所采用的的字符集编码在Servelt规范中没有明确规定,Tomcat的ServletRequest的getParameter方法默认采用ISO8859-1进行URL解码,无法返回正确的中文参数信息。
-
对于POST方式下的“application/x-www-form-urlencoded”编码格式的实体内容。getParameter等方法以ServletRequest对象的getCharacterEncoding方法返回的字符集编码对其进行解码。getCharacterEncoding返回值为null。ServletRequest的getParameter方法默认采用ISO8859-1进行URL解码,无法返回正确的中文参数信息。
-
getParameter等方法以setCharacterEncoding方法设置的字符集编码对实体内容进行URL解码,可以获得争取的中文参数信息。这个设置方法之影响getParameter方法对POST方式下的“application/x-www-form-urlencoded”编码格式的实体内容进行URL解码的结果。