1.9.2 HttpServletRequest接口

HttpServletRequest表示HTTP环境中的Servlet请求。它扩展javax.servlet.ServletRequest接口,并添加了几个方法。

HttpServletRequest接口新增方法

HttpServletRequest接口新增的部分方法如表所示:

方法 描述
java.lang.String getContextPath() 返回表示请求上下文的请求URI部分。
Cookie[] getCookies() 返回一个Cookie对象数组。
java.lang.String getHeader(java.lang.String name) 返回指定HTTP标题的值。
java.lang.String getMethod() 返回生成这个请求的HTTP方法名称。
java.lang.String getQueryString() 返回请求URL中的查询字符串。
HttpSession getSession() 返回与这个请求相关的会话对象。如果没有,将创 建一个新的会话对象。
HttpSession getSession(boolean create) 返回与这个请求相关的会话对象。如果有,并且 create参数为true,将创建一个新的会话对象。

1.9 Http Servlets

不说全部,至少大多数应用程序都要与HTTP结合 起来使用。这意味着可以利用HTTP提供的特性。 javax.servlet.http包是Servlet API中的第二个包,其中包 含了用于编写Servlet应用程序的类和接口。 javax.servlet.http中的许多类型都覆盖javax.servlet中的 类型。 下图展示了javax.servlet.http中的主要类型。
这里有一张图片

1.9.1 HttpServlet

HttpServlet类覆盖了javax.servlet.GenericServlet类。 使用HttpServlet时,还要借助分别代表Servlet请求和Servlet响应的HttpServletRequestHttpServletResponse对象。HttpServletRequest接口扩展 javax.servlet.ServletRequestHttpServletResponse扩展 javax.servlet.ServletResponseHttpServlet覆盖GenericServlet中的service方法,并通过下列签名再添加一个service方法:

1
2
3
protected void service(HttpServletRequest request, 
HttpServletResponse response)
throws ServletException, java.io.IOException

service方法和javax.servlet.Servletservice方法之间的区别在于方法的参数列表上,前者使用HttpServletRequestHttpServletResponse作为形式参数,而不是ServletRequestServletResponse。 像往常一样,Servlet容器调用javax.servlet.Servlet中原始的service方法。HttpServlet中的编写service方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void service(ServletRequest req,
ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException(
"non-HTTP request or respons e");
}
service(request, response);
}

原始的service方法将Servlet容器创建的requestresponse对象分别转换成HttpServletRequestHttpServletResponse类对象,并调用新的service方法。这种转换总是会成功的,因为Servlet容器在调用Servlet的service方法时, Servlet容器总会传入一个HttpServletRequest和一个HttpServletResponse,预备使用HTTP。所以在通过实现 javax.servlet.Servlet,或者扩展(继承)javax.servlet.GenericServlet的创建的Servlet类中,也可以将传给service方法的servletRequestservletResponse分别转换成HttpServletRequestHttpServletResponse
然后,HttpServlet中的service方法会检验用来发送请求的HTTP方法(通过调用request.getMethod),并调 用以下方法之一:doGetdoPostdoHeaddoPutdoTracedoOptionsdoDelete。这7种方法中,每一种 方法都表示一个HTTP方法。doGet和doPost是最常用 的。因此,不再需要覆盖service方法了,只要覆盖 doGet或者doPost,或者覆盖doGet和doPost即可

总之,HttpServlet有两个特性是GenericServlet所不 具备的:

  • 不用覆盖service方法,而是覆盖doGet方法或者doPost方法, 或者同时覆盖doGetdoPost。在少数情况下,还会覆盖以下任意方法:doHeaddoPutdoTracedoOptionsdoDelete
  • 使用HttpServletRequestHttpServletResponse,而不 是ServletRequestServletResponse

1.7 ServletContext接口

  • ServletContext官方叫servlet上下文,每个应用(或者叫项目,工程,…)都会有一个ServletContext对象与之关联.工程内部的所有Servlet都共享这个ServletContext对象,因此可以叫做全局应用程序共享对象
  • 当容器分布在多个虚拟机上时,web应用在所分布的每个虚拟机上都拥有一个ServletContext实例与之关联。缺省情况下,ServletContext不是分布式的,并且只存在于一个虚拟机上。

ServletContext的作用

通过ServletContext可以访问应用范围的初始化参数和属性:

Servlet中如何获取ServletContext对象

通过在ServletConfig中调用getServletContext方法, 可以获得ServletContext

操作属性方法

保存在ServletContext中的对象被称作属性,ServletContext中的下列方法负责处理属性:

方法 描述
java.lang.Object getAttribute(java.lang.String name) 获取属性
java.util.Enumeration<java.lang.String> getAttributeNames() 获取属性枚举
void setAttribute(java.lang.String name, java.lang.Object object) 设置属性
void removeAttribute(java.lang.String name) 移除属性

参考资料

https://baike.baidu.com/item/servletContext/6758455
https://blog.csdn.net/qq_36371449/article/details/80314024

1.6 ServletConfig接口

接口方法

方法 描述
String getInitParameter(String name) 获取具有给定名称的初始化参数的值。
Enumeration<String> getInitParameterNames() Enumeration<String>对象的形式返回servlet的初始化参数的名称,如果servlet没有初始化参数,则返回空的Enumeration
ServletContext getServletContext() 返回与所在项目关联的ServletContext的引用。
String getServletName() 返回此servlet实例的名称。

接口描述

Servlet容器初始化Servlet时,Servlet容器会给 Servletinit方法传入一个ServletConfig对象。ServletConfig 封装可以通过@WebServlet或者通过部署描述符来传给Servlet 的配置信息。这样传入的每一条信息就叫一个初始参数。一个初始参数有keyvalue两个部分。 为了从Servlet内部获取到初始参数的值,要在 Servlet容器传给Servletinit方法的ServletConfig中调用 getInitParameter方法。getInitParameter的方法签名如 下:

1
java.lang.String getInitParameter(java.lang.String name)

此外,getInitParameterNames方法则是返回所有初 始参数名称的一个Enumeration

1
java.util.Enumeration<java.lang.String> getInitParameterNames()

例如,为了获取contactName参数值,要使用下面的方法签名:

1
2
String contactName = 
servletConfig.getInitParameter("contactName");

getInitParametergetInitParameterNames外,ServletConfig还提供了另一个很有用的方法: getServletContext。利用这个方法可以从Servlet内部获取ServletContext。关于这个对象的深入探讨,请查阅本 章1.7节。

实例

下面举一个ServletConfig的范例,在app01a项目中的src目录中的app01a包中添加 一个名为ServletConfigDemoServletServlet。这个新的 Servlet如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
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"},
// 设置Servlet的初始化参数
initParams =
{
@WebInitParam(
name = "admin",
value = "Harry Taciak"
),
@WebInitParam(
name = "email",
value = "admin@example.com"
)}
)
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 servletConfig = getServletConfig();
// 获取Servlet的初始化参数
String admin = servletConfig.getInitParameter("admin");
String email = servletConfig.getInitParameter("email");
// 获取Servlet的名称
String servletName = servletConfig.getServletName();
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
writer.print("<html>"
+ "<head>"
+ "</head>"
+ "<body>"
+ "Admin: " + admin + "<br/>"
+ "Email: " + email + "<br/>"
+ "ServletName: " + servletName + "<br/>"
+ "</body>"
+ "</html>");
}
@Override
public String getServletInfo()
{
return "ServletConfig demo";
}
// 实现接口方式必须实现该接口中的所有方法
@Override
public void destroy()
{}
}

@WebServletinitParams属性 中,给Servlet传入了两个初始参数(adminemail):

1
2
3
4
5
6
7
8
9
10
11
12
13
@WebServlet(
name = "ServletConfigDemoServlet",
urlPatterns ={"/servletConfigDemo"},
initParams ={
@WebInitParam(
name = "admin",
value = "Harry Taciak"
),
@WebInitParam(
name = "email",
value = "admin@example.com"
)}
)

运行效果

注意这里的urlPatterns是对于项目路径而言,/servletConfigDemoHello项目中的路径.加上项目路径,可以调用ServletConfigDemoServlet,如下所示:
http://localhost:8080/app01a/servletConfigDemo
浏览器将会显示以下文本:

1
2
3
Admin: Harry Taciak
Email: admin@example.com
ServletName: ServletConfigDemoServlet

部署描述符中设置Servlet的初始化参数

另一种方法是,在部署描述符中传入初始参数。使用部署描述符设置Servlet的初始参数,比使用@WebServlet更容易,因为部署描述符是一个文本文件,不需要重新编译Servlet,就可以对它进行编辑。部署描述符将在本章后续”使用部署描述符”小节以及第13章中详细讲解。

总结

  • ServletConfig操作的是Servlet的初始化参数。

1.5 ServletResponse接口

javax.servlet.ServletResponse接口表示一个Servlet响应。在调用Servletservice方法前,Servlet容器首先创建一个ServletResponse类对象,并将它作为第二个参数传给 service方法。ServletResponse隐藏了向浏览器发送响应的复杂过程。

getWriter方法

ServletResponse中有一个getWriter方法,它返回了一个可以向客户端发送文本的 java.io.PrintWriter类对象。默认情况下,PrintWriter对象使用的编码是 ISO-8859-1编码。
**在向客户端发送响应时,大多数时候是将响应作为 HTML发送的**。因此,你必须非常熟悉HTML`。

getOutputStream方法

还有一个方法可以用来向浏览器发送输出,它就是** getOutputStream方法。但这个方法是用于发送二进制数据**的,因此,大多数情况使用的是getWriter,而不是getOutputStream

setContentType方法

在发送任何HTML标签前,应该先调用setContentType方法,设置响应的内容类型,并将"text/html"作为一个参数传入。这是在告诉浏览器, 内容类型为HTML。在没有内容类型的情况下,大多数浏览器会默认将响应渲染成HTML。但是,如果没有设置响应内容类型,有些浏览器就会将HTML标签显示为普通文本。

1.4 ServletRequest接口

对于每一个HTTP请求,Servlet容器都会创建一个 ServletRequest实例,并将它作为参数传给Servletservice方法ServletRequest封装了关于这个请求的信息

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) 返回指定请求参数的值。

getParameter是在ServletRequest中最常用的方法。该方法通常用于返回HTML表单域的值。在本章后续 的“处理表单”小节中,将会学到如何获取表单值。 getParameter也可以用于获取查询字符串的值。例如,利用下面的URI调用Servlethttp://domain/context/servletName?id=123用下面这个语句,可以通过Servlet内部获取id值:

1
String id = request.getParameter("id"); 

注意,如果该参数不存在,getParameter方法将返回null。除了getParameter外,还可以使用 getParameterNamesgetParameterMapgetParameterValues获取表单域的名、值以及查询字符 串。这些方法的使用范例请参阅“Http Servlets”小节。

1.3 编写基础的Servlet应用程序

其实,编写Servlet应用程序出奇简单。只需要创建一个目录结构,并把Servlet类放在某个目录下。本节将教你如何编写一个名为app01aServlet应用程序。最初,它会包含一个Servlet,即MyServlet,其效果是向用户发出一条问候。 要运行Servlet,还需要一个Servlet容器Tomcat是一个开源的Servlet容器,它是免费的,并且可以在任何能跑Java的平台上运行。如果你到现在都还没有安装 Tomcat,先安装Tomcat.

1.3.1编写和编译Servlet类

确定你的机器上有了Servlet容器后,下一步就要编写和编译一个Servlet类。本例中的Servlet类是MyServlet,按照惯例,Servlet类的名称要以Servlet作为后缀。

MyServlet类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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("<html><head></head>" +
"<body>Hello from " +
servletName +
"</body></html>");
}
@Override
public void destroy()
{}
}

代码详解

WebServlet注解

1
2
3
4
@WebServlet(
name = "MyServlet",
urlPatterns = { "/my" }
)

WebServlet注解用来声明一个Servlet。命名Servlet时,还可以告诉容器,是哪个URL调用这个Servletname属性是可选的,如有,通常用Servlet类的名称。重要的是urlPatterns属性,它也是可选的,但是一般都是有的。在MyServlet中,urlPatterns告诉容器,当URL样式为:/my时应该调用MyServlet
注意,**URL样式必须用一个正斜杠开头**。

init方法

Servletinit方法只被调用一次,并将private transient变量ServletConfig设为传给该方法的ServletConfig对象:

1
2
3
4
5
6
7
private transient ServletConfig servletConfig;
@Override
public void init(ServletConfig servletConfig)
throws ServletException
{
this.servletConfig = servletConfig;
}

如果想通过Servlet内部使用ServletConfig,只需要将被传入的ServletConfig赋给一个类变量。

service方法

service方法发送字符串“Hello from MyServlet”给浏览器。对于每一个针对Servlet进来的HTTP请求,都会调用service方法

Servlet程序目录结构

Servlet应用程序必须在某一个目录结构下部署。图1.2展示了app01a的应用程序目录。
这个目录结构最上面的 app01a目录就是应用程序目录。在应用程序目录下,是WEB-INF目录。它有两个子目录:

  • classesServlet类及其他Java类必须放在这里面。类以下的目录反映了类包的结构。在图1.2中,只部署了一个类:app01a.MyServlet
  • libServlet应用程序所需的JAR文件要在这里部署。但Servlet APIJAR文件不需要在这里部署,因为Servlet容器已经有它的备份。在这个应用程序中,lib目录是空的。空的lib目录可以删除。

Servlet/JSP应用程序一般都有JSP页面、HTML文件、图片文件以及其他资料。这些应该放在应用程序目录下,并且经常放在子目录下。例如,所有的图片文件可以放在一个image目录下,所有的JSP页面可以放在jsp目录下,等等。
放在应用程序目录下的任何资源,用户只要输入资源URL,都可以直接访问到

WEB-INF目录

如果想让某一个资源可以被Servlet访问,但不可以被用户访问,那么就要把它放在WEB-INF目录下

手动部署

现在,准备将应用程序部署到Tomcat。使用Tomcat时,一种部署方法是将应用程序目录复制到Tomcat安装目录下的webapps目录中。也可以通过在Tomcatconf目录中编辑server.xml文件实现部署,或者单独部署一个XML文件,这样就不需要编辑server.xml了。其他的Servlet容器可能会有不同的部署规则。

部署为WAR文件

部署Servlet/JSP应用程序时,建议将它部署成一个WAR文件。WAR文件其实就是以.war作为扩展名的JAR文件。利用带有JDK或者类似WinZip工具的JAR软件,都可以创建WAR文件。然后,将WAR文件复制到Tomcatwebapps目录下。当开始启动Tomcat时,Tomcat就会自动解压这个war文件。部署成WAR文件在所有Servlet容器中都适用。我们将在第13章讨论更多关于部署的细节。

1.3.3调用Servlet

要测试这个Servlet,需要启动或者重启Tomcat,并在浏览器中打开下面的URL(假设Tomcat配置为监听端口8080,这是它的默认端口):
http://localhost:8080/app01a/my
浏览器显示效果如下:
这里有一张图片

1.2 Servlet

Servlet接口中定义了以下5个方法:

方法 描述
void init(ServletConfig config) throws ServletException 初始化方法
void service(ServletRequest request,ServletResponse response) throws ServletException, java.io.IOException 响应请求方法
void destroy() 销毁Servlet
java.lang.String getServletInfo() 返回Servlet的描述信息
ServletConfig getServletConfig() 返回由Servlet容器传给init方法的ServletConfig对象

注意,编写Java方法签名时,如果方法的返回值类型或者抛出的异常类型和定义这个方法的类不再同一个包下,则返回值类型或异常类型要使用类全名。 正因为如此,在service方法的签名中,由于javax.servlet.ServletException这个类与当前类Servlet位于同一个包中,所以ServletException,可以不用写类全名,而java.io.Exception和当前类javax.servlet.Servlet,不再同一个包下,所以Exception类要写完整的名称。

生命周期方法

initservicedestroy是生命周期方法。Servlet容器根据以下规则调用这3个方法:

  • init方法,当该Servlet第一次被请求时,Servlet容器会调用这个方法。这个方法只调用这一次,在后续请求中将不会再被调用。我们可以利用这个方法执行相应的初始化工作。 调用这个方法时,Servlet容器会传入一个 ServletConfig对象的引用作为init方法的参数。一般来说,你会在init方法体中把ServletConfig对象引用赋给一个类变量。
  • service方法,每当请求Servlet时,Servlet容器就会调用这个service方法。当第一次请求Servlet时,Servlet容器要同时调用init方法和service方法。后续的请求将只调用service方法。
  • destroy方法,当要销毁Servlet时,Servlet容器就会调用这个方法。当要卸载应用程序,或者当要关闭 Servlet容器时,就会发生这种情况。一般会在这个方法中编写清除代码。

非生命周期方法

Servlet中的另外两个方法是非生命周期方法,即 getServletInfogetServletConfig方法:

  • getServletInfo方法,这个方法会返回Servlet的描述。你可以返回有用的或者为null的任意字符串。
  • getServletConfig方法,这个方法会返回由Servlet容器传给init方法的ServletConfig对象。但是,为了让 getServletConfig方法返回一个非null值,必须将传给init 方法的ServletConfig赋给一个类变量。 ServletConfig将在本章的1.6节中讲解。

注意线程安全性。**Servlet实例会被一个应用程序中的所有用户共享**,因此不建议使用类级变量,除非它们是只读的,或者是java.util.concurrent.atomic包的成员。 下一节“编写基础的Servlet应用程序”将介绍如何编写Servlet实现。

第1章 Servlets

Servlet API是开发Servlet的主要技术。掌握Servlet API是成为一名强大的Java web开发者的基本条件,你必须熟悉Servlet API中定义的核心接口和类。 本章介绍了Servlet API,并教你如何编写第一个 Servlet

1.1 Servlet API概览

Servlet API有以下4个Java包:

  • javax.servlet,其中包含定义ServletServlet容器之间契约的类和接口。
  • javax.servlet.http,其中包含定义HTTP ServletServlet容器之间契约的类和接口。
  • javax.servlet.annotation,其中包含标注ServletFilterListener的标注。它还为被标注元件定义元数据。
  • javax.servlet.descriptor,其中包含提供程序化登录 web应用程序的配置信息的类型。

本章主要关注javax.servletjavax.servlet.http的成员。

Servlet技术的核心是Servlet,它是所有Servlet类必须直接或间接实现的一个接口。在编写实现ServletServlet类时,直接实现它。在扩展实现这个接口的类时,间接实现它。 Servlet接口定义了ServletServlet容器之间的契约。

这个契约归结起来就是,**Servlet容器将Servlet类载入内存,并在Servlet实例上调用具体的方法在一个应用程序中,每种Servlet类型只能有一个实例**。

Servlet请求响应过程

用户请求到达时,Servlet容器会调用Servletservice方法,并传入一个ServletRequest实例和一个ServletResponse实例作为service方法的参数。ServletRequest中封装了当前的HTTP请求,因此,Servlet开发人员不必解析和操作原始的HTTP数据。ServletResponse表示当前用户的HTTP响应,通过ServletResponse,使得将响应发回给用户变得十分容易。

对于每一个应用程序,Servlet容器还会创建一个 ServletContext实例。这个对象中封装了上下文(应用程序)的环境详情。每个上下文只有一个ServletContext。 每个Servlet实例也都有一个封装Servlet配置的 ServletConfig
下面来看Servlet接口。上面提到的其他接口,将在本章的其他小节中讲解。

内容提要

ServletJSP是开发Java Web应用程序的两种基本技术。Spring MVCSpring框架中用于Web应用快速开发的一个模块,是当今最流行的Web开发框架之一。
本书是ServletJSPSpring MVC的学习指南。全书内容分为两个部分,第一部分主要介绍ServletJSP基础知识和技术,包括第1章至第15章;第2部分主要介绍Spring MVC,包括第16章至第24章。最后,附录部分给出了Tomcat安装和配置指导,还介绍了Servlet and JSP注解以及SSL证书。
本书内容充实、讲解清晰,非常适合Web开发者尤其是基于JavaWeb应用开发者阅读。