《Servlet、JSP和Spring MVC》学习笔记--原书第一章
该系列笔记主要为《Servlet、JSP和Spring MVC》的学习笔记,全书通俗易懂,适合对Servlet、JSP等零基础的小白同学。虽然笔记中包含大量图书原文,但笔者已经尽量去掉自认为无用或是啰嗦的翻译,尽量突出重点,并在适当地方加入了一定补充。书中涉及到的每一个例子,均基于Maven进行了重新构建,尽可能适合现在的开发环境。在本篇教程中的源码均上传至github,并根据章节进行分类整理,便于读者一起学习。
准备
MVC:
- Model
- View
- Controller
Servlet/JSP应用架构

HTTP
HTTP请求
一个HTTP请求包含三部分内容:
- 方法-URI-协议/版本
- 请求头信息
- 请求正文
HTTP响应
同HTTP请求一样,HTTP响应包含三部分:
- 协议—状态码—描述
- 响应头信息
- 响应正文
第一章 Servlets
必须熟悉Servlet API中定义的核心接口和类。
1.1 Servlet API概览
Servlet API包含以下4个包:
- javax.servlet
- javax.servlet.http
- javax.servlet.annotation
- javax.servlet.descriptor
1.2 Servlet
Servlet接口中的五个方法:
void init(ServletConfig config) throws ServletException
void service(ServletRequest request, ServletResponse response)
throws ServletException, java.io.IOException
void destroy()
java.lang.String getServletInfo()
ServletConfig getServletConfig()
注意,编写Java方法签名的惯例是,对于与包含该方法的类型不处于同一个包中的类型,要使用全类名。
生命周期方法:
- init:当该Servlet第一次被请求时,Servlet容器会调用这个方法。这个方法在后续请求中不会再被调用。我们可以利用这个方法执行相应初始化工作。调用这个方法时Servlet容器会传入一个ServletConfig。
- service:每当请求Servlet时,Servlet容器就会调用这个方法。编写代码时,是假设Servlet要在这里被请求。第一次请求Servlet时,Servlet容器调用init方法和Service方法。后续的请求将只调用Service方法。
- destroy:当要销毁Servlet时,Servlet容器就会调用这个方法。当要卸载应用程序,或者当要关闭Servlet容器时,就会发生这种情况。一般会在这个方法中编写清除代码。
非生命周期方法:
- getServletInfo:这个方法会返回Servlet的描述。
- getServletConfig:这个方法会返回由Servlet容器传给init方法的ServletConfig。
注意线程安全性。Servlet实例会被一个应用程序中的所有用户共享,因此不建议使用类级变量,除非它们是只读的,或者是java.util.concurrent.atomic包的成员。
1.3 编写基础的Servlet程序
要运行Servlets,还需要一个Servlet容器。Tomcat是一个开源的Servlet容器。
1.3.1 编写和编译Servlet类
本例中的Servlet类是MyServlet。按照惯例,Servlet类的名称要以Servlet作为后缀。
先添加依赖:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
MyServlet.java如下:
@WebServlet(name = "MyServlet", urlPatterns = {"/my"})
public class MyServlet implements Servlet {
// transient关键词表示该属性不参与序列化过程
private transient ServletConfig servletConfig;
@Override
public void init(ServletConfig servletConfig)
throws ServletException {
this.servletConfig = servletConfig;
}
@Override
public ServletConfig getServletConfig() {
return servletConfig;
}
@Override
public void service(ServletRequest servletRequest,
ServletResponse servletResponse)
throws ServletException, IOException {
String servletName = servletConfig.getServletName();
servletResponse.setContentType("text/html");
PrintWriter writer = servletResponse.getWriter();
writer.print("<html><head></head>" +
"<body>Hello from " + servletName +
"</body></html>");
}
@Override
public String getServletInfo() {
return "My Servlet";
}
@Override
public void destroy() {
}
}
@WebServlet注解用于声明一个Servlet,通过源码整理的该注解属性如下:
| 属性 | 描述 |
|---|---|
| name | The name of the servlet |
| value | The URL patterns of the servlet |
| urlPatterns | The URL patterns of the servlet |
| loadOnStartup | The load-on-startup order of the servlet |
| initParams | The init parameters of the servlet |
| asyncSupported | Declares whether the servlet supports asynchronous operation mode. |
| smallIcon | The small-icon of the servlet |
| largeIcon | The large-icon of the servlet |
| description | The description of the servlet |
| displayName | The display name of the servlet |
正如前边所描述,Servlet第一次被调用后将会调用init方法,对servletConfig进行赋值。
@Override
public void init(ServletConfig servletConfig)
throws ServletException {
this.servletConfig = servletConfig;
}
每个HTTP请求都会调用service方法:
@Override
public void service(ServletRequest servletRequest,
ServletResponse servletResponse)
throws ServletException, IOException {
String servletName = servletConfig.getServletName();
servletResponse.setContentType("text/html");
PrintWriter writer = servletResponse.getWriter();
writer.print("<html><head></head>" +
"<body>Hello from " + servletName +
"</body></html>");
}
1.3.2 应用程序目录结构
该部分的大部分功能由我的IDEA执行,所以这里不再讨论应用程序目录结构。
1.3.3 调用Servlet
使用tomcat运行:

注意在maven的pom.xml文件中需要修改打包方法为war
<packaging>war</packaging>
运行结果如下:

到这里,我们已经完整运行了一个Servlet的基本程序。
1.4 ServletRequest
对于每一个HTTP请求,Servlet容器都会创建一个ServletRequest实例,并将它传给Servlet的Service方法。ServletRequest封装了关于这个请求的信息。
以下介绍部分ServletRequest接口中的方法:
| Method | 描述 |
|---|---|
| public int getContentLength() | 返回请求主体的字节数 |
| public String getContentType() | 返回请求主体的MIME类型 |
| public String getParameter(String name) | 返回指定请求参数的值。 |
| public String getProtocol() | 返回这个HTTP请求的协议名称和版本 |
getParameter()方法最为常用:
-
该方法通常用于返回HTML表单域的值。在本章后续的“处理表单”小节中,将会学到如何获取表单值。
-
也可以用于获取查询字符串的值。例如,利用下面的URI调用Servlet:
http://domain/context/servletName?id=123// 通过该方法获取id,如果不存在返回null String id = request.getParameter("id");
除了getParameter外,还可以使用getParameterNames、getParameterMap和getParameterValues获取表单域名、值以及查询字符串。这些方法的使用范例请参阅“Http Servlets”小节。
1.5 ServletResponse
javax.servlet.ServletResponse接口表示一个Servlet响应。在调用Servlet的service方法前,Servlet容器首先创建一个ServletResponse,并将它作为第二个参数传给service方法。ServletResponse隐藏了向浏览器发送响应的复杂过程。
在ServletResponse中定义的方法之一是getWriter方法,它返回了一个可以向客户端发送文本的java.io.PrintWriter。默认情况下,PrintWriter对象使用ISO-8859-1编码。
还有一个方法可以用来向浏览器发送输出,它就是getOutputStream。但这个方法是用于发送二进制数据的,因此,大多数情况使用的是getWriter,而不是getOutputStream。
在发送任何HTML标签前,应该先调用setContentType方法,设置响应的内容类型,并
将“text/html”作为一个参数传入。这是在告诉浏览器,内容类型为HTML。在没有内容类型的情况下,大多数浏览器会默认将响应渲染成HTML。但是,如果没有设置响应内容类型,有些浏览器就会将HTML标签显示为普通文本。使用范例见1.3.1中的示例代码。
1.6 ServletConfig
当Servlet容器初始化Servlet时,Servlet容器会给Servlet的init方法传入一个ServletConfig。ServletConfig封装可以通过@WebServlet或者部署描述符传给Servlet
的配置信息。这样传入的每一条信息就叫一个初始参数。一个初始参数包含:key和value。
为了从Servlet内部获取到初始参数的值,要在Servlet容器传给Servlet的init方法的ServletConfig中调用getInitParameter方法。
java.lang.String getInitParameter(java.lang.String name)
此外,getInitParameterNames方法则是返回所有初始参数名称的一个Enumeration:
java.util.Enumeration<java.lang.String> getInitParameterNames()
除getInitParameter和getInitParameterNames外,ServletConfig还提供了另一个很有用的方法:getServletContext。利用这个方法可以从Servlet内部获取ServletContext。关于这个对象的深入探讨,请查阅本章1.7节。
下面介绍一个名为ServletConfigDemoServlet的Servlet范例:
@WebServlet(name = "ServletConfigDemoServlet",
urlPatterns = {"/servletConfigDemo"},
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 void init(ServletConfig config) throws ServletException {
this.servletConfig = config;
}
@Override
public ServletConfig getServletConfig() {
return servletConfig;
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
ServletConfig config = getServletConfig();
String admin = config.getInitParameter("admin");
String email = config.getInitParameter("email");
res.setContentType("text/html");
PrintWriter writer = res.getWriter();
writer.print("<html><head></head><body>" +
"Admin:" + admin +
"<br/>Email:" + email +
"</body></html>");
}
@Override
public String getServletInfo() {
return "SevletConfig Demo";
}
@Override
public void destroy() {
}
}
如上,,在@WebServlet的initParams属性中,给Servlet传入了两个初始参数(admin和email):
@WebServlet(name = "ServletConfigDemoServlet",
urlPatterns = {"/servletConfigDemo"},
initParams = {
@WebInitParam(name = "admin", value = "Harry Taciak"),
@WebInitParam(name = "email", value = "admin@example.com")
}
)
通过浏览器,我们可以获得如下效果:

另一种方法是,在部署描述符中传入初始参数。在这里使用部署描述符,比使用@WebServlet更容易,因为部署描述符是一个文本文件,不需要重新编译Servlet类,就可以对它进行编辑。
部署描述符将在本章后续“使用部署描述符”小节以及第13章中详细讲解。
1.7 ServletContext
ServletContext表示Servlet应用程序。每个Web应用程序只有一个上下文。在将一个应用程序同时部署到多个容器的分布式环境中,每台Java虚拟机上的Web应用都会有一个ServletContext对象。
通过在ServletConfig中调用getServletContext方法,可以获得ServletContext。
有了ServletContext,就可以共享从应用程序中的所有资料处访问到的信息,并且可以动态注册Web对象。前者将对象保存在ServletContext中的一个内部Map中。保存在ServletContext中的对象被称作属性。
ServletContext中的下列方法负责处理属性:
Object getAttribute(String name)
Enumeration<String> getAttributeNames()
void setAttribute(String name, Object object)
void removeAttribute(String name)
1.8 GenericServlet
上述例子演示了如何使用Servlet接口来编写Servlet,需要实现全部的方法,同时需要保持ServletConfig到类级变量中。
本着尽可能使代码简单的原则, GenericServlet实现了Servlet和ServletConfig接口,并完成以下任务:
- 将init方法中的ServletConfig赋给一个类级变量,以便可以通过调用getServletConfig获取
- 为Servlet接口中的所有方法提供默认的实现。
- 提供方法,包围ServletConfig中的方法。
对于GenericServlet中的init()方法,如果需要覆盖该方法,需要调用this.init()
这里使用GenericServlet改写1.6中的Demo:
public class GenericServletDemoServlet extends GenericServlet {
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
ServletConfig config = getServletConfig();
String admin = config.getInitParameter("admin");
String email = config.getInitParameter("email");
res.setContentType("text/html");
PrintWriter writer = res.getWriter();
writer.print("<html><head></head><body>" +
"Admin:" + admin +
"<br/>Email:" + email +
"</body></html>");
}
}
运行结果保持一致:

使GenericServlet是对Servlet一个很好的加强,但它也不常用,因为它毕竟不像HttpServlet那么高级。HttpServlet才是主角,在现实的应用程序中被广泛使用。关于它的详情,请查阅1.9节。
1.9 Http Servlets
javax.servlet.http中的许多类型都覆盖了javax.servlet中的类型。图1.5展示了javax.servlet.http中的主要类型。

1.9.1 HttpServlet
HttpServlet类覆盖了javax.servlet.GenericServlet类。使用HttpServlet时,还要借助分别代表Servlet请求和Servlet响应的HttpServletRequest和HttpServletResponse对象。HttpServletRequest接口扩展javax.servlet.ServletRequest,HttpServletResponse扩展
javax.servlet.ServletResponse。
HttpServlet覆盖GenericServlet中的Service方法,并通过下列签名再添加一个Service方法:
protected void service(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, java.io.IOException
新service方法的区别在于接收新类型的输入参数。
像往常一样,Servlet容器调用javax.servlet.Servlet中原始的service方法。HttpServlet中的编写方法如下:
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
if (!(req instanceof HttpServletRequest &&
res instanceof HttpServletResponse)) {
throw new ServletException("non-HTTP request or response");
}
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
service(request, response);
}
- 原始的service方法将request和response分别转换为HttpServletRequest和HttpServletResponse,并调用新的service方法.
- HttpServlet中的Service方法会检验用来发送请求的HTTP方法,并调用doGet、doPost、doHead、doPut、doTrace、doOptions和doDelete方法之一。因此这里不需要再覆盖service方法,只需要覆盖doGet或者doPost方法。
HttpServlet有两个特性是GenericServlet所不具备的:
- 不用覆盖Service方法,而是覆盖doGet或者doPost。在少数情况下,还会覆盖以下任意方法:doHead、doPut、doTrace、doOptions和doDelete。
- 使用HttpServletRequest和HttpServletResponse,而不是ServletRequest和ServletResponse。
1.9.2 HttpServletRequest
继承javax.servlet.ServletRequest接口,并添加了几个方法。新增部分方法如下:
| 方法名 | 描述 |
|---|---|
| String getContextPath() | 返回表示请求上下文的请求URI部分 |
| Cookie[] getCookies() | 返回一个Cookie对象数组。 |
| String getHeader(String name) | 返回指定HTTP标题的值 |
| String getMethod() | 返回生成这个请求的HTTP方法名称 |
| String getQueryString() | 返回请求URL中的查询字符串 |
| HttpSession getSession() | 返回与这个请求相关的会话对象。如果没有,将创建一个新的会话对象 |
| HttpSession getSession(boolean create) | 返回与这个请求相关的会话对象。如果有,并且create参数为True,将创建一个新的会话对象。 |
1.9.3 HttpServletResponse
继承javax.servlet.ServletResponse接口,并添加了几个方法。新增部分方法如下:
| 方法 | 描述 |
|---|---|
| void addCookie(Cookie cookie) | 给这个响应对象添加一个cookie。 |
| void addHeader(String name, String value) | 给这个响应对象添加一个header。 |
| void sendRedirect(String location) | 发送一条响应码,将浏览器跳转到指定的位置。 |
下面的章节将进一步学习这些方法。
1.10 处理HTML表单
- HTML输入域(文本域、隐藏域或者密码域)或者文本区的值,会被当作字符串发送到服务器。空的输入域或者文本区会发送空的字符串。因此,有输入域名称的,ServletRequest.getParameter绝对不会返回null。
- HTML的select元素也向header发送了一个字符串。如果select元素中没有任何选项被选中,那么就会发出所显示的这个选项值。包含多个值的select元素(允许选择多个选项并且
用<select multiple>表示的select元素)发出一个字符串数组,并且必须通过SelectRequest.getParameterValues进行处理。 - 复选框比较奇特。核查过的复选框会发送字符串“on”到服务器。未经核查的复选框则不向服务器发送任何内容,ServletRequest.getParameter(fieldName)返回null。
- 单选框将被选中按钮的值发送到服务器。如果没有选择任何按钮,将没有任何内容被发送到服务器,并且ServletRequest.getParameter(fieldName)返回null。
如果一个表单中包含多个输入同名的元素,那么所有值都会被提交,并且必须利用
ServletRequest.getParameterValues来获取它们。ServletRequest.getParameter将只返回最后一个值。
FormServlet类示范了如何处理HTML表单。它的doGet方法将一个Order表单发送到浏览器。它的doPost方法获取到所输入的值,并将它们输出。
@WebServlet(name = "FormServlet", urlPatterns = {"/form"})
public class FormServlet extends HttpServlet {
private static final String TITLE = "Order Form";
@Override
protected void doGet(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
writer.println("<html>");
writer.println("<head>");
writer.println("<title>" + TITLE + "</title></head>");
writer.println("<body><h1>" + TITLE + "</h1>");
writer.println("<form method='post'>");
writer.println("<table>");
writer.println("<tr>");
writer.println("<td>Name:</td>");
writer.println("<td><input name='name'/></td>");
writer.println("</tr>");
writer.println("<tr>");
writer.println("<td>Address:</td>");
writer.println("<td><textarea name='address' "
+ "cols='40' rows='5'></textarea></td>");
writer.println("</tr>");
writer.println("<tr>");
writer.println("<td>Country:</td>");
writer.println("<td><select name='country'>");
writer.println("<option>United States</option>");
writer.println("<option>Canada</option>");
writer.println("</select></td>");
writer.println("</tr>");
writer.println("<tr>");
writer.println("<td>Delivery Method:</td>");
writer.println("<td><input type='radio' " +
"name='deliveryMethod'"
+ " value='First Class'/>First Class");
writer.println("<input type='radio' " +
"name='deliveryMethod' "
+ "value='Second Class'/>Second Class</td>");
writer.println("</tr>");
writer.println("<tr>");
writer.println("<td>Shipping Instructions:</td>");
writer.println("<td><textarea name='instruction' "
+ "cols='40' rows='5'></textarea></td>");
writer.println("</tr>");
writer.println("<tr>");
writer.println("<td> </td>");
writer.println("<td><textarea name='instruction' "
+ "cols='40' rows='5'></textarea></td>");
writer.println("</tr>");
writer.println("<tr>");
writer.println("<td>Please send me the latest " +
"product catalog:</td>");
writer.println("<td><input type='checkbox' " +
"name='catalogRequest'/></td>");
writer.println("</tr>");
writer.println("<tr>");
writer.println("<td> </td>");
writer.println("<td><input type='reset'/>" +
"<input type='submit'/></td>");
writer.println("</tr>");
writer.println("</table>");
writer.println("</form>");
writer.println("</body>");
writer.println("</html>");
}
@Override
protected void doPost(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
writer.println("<html>");
writer.println("<head>");
writer.println("<title>" + TITLE + "</title></head>");
writer.println("<body><h1>" + TITLE + "</h1>");
writer.println("<table>");
writer.println("<tr>");
writer.println("<td>Name:</td>");
writer.println("<td>" + req.getParameter("name")
+ "</td>");
writer.println("</tr>");
writer.println("<tr>");
writer.println("<td>Address:</td>");
writer.println("<td>" + req.getParameter("address")
+ "</td>");
writer.println("</tr>");
writer.println("<tr>");
writer.println("<td>Country:</td>");
writer.println("<td>" + req.getParameter("country")
+ "</td>");
writer.println("</tr>");
writer.println("<tr>");
writer.println("<td>Shipping Instructions:</td>");
writer.println("<td>");
String[] instructions = req.
getParameterValues("instruction");
if (instructions != null) {
for (String instruction : instructions) {
writer.println(instruction + "<br/>");
}
}
writer.println("</td>");
writer.println("</tr>");
writer.println("<tr>");
writer.println("<td>Delivery Method:</td>");
writer.println("<td>"
+ req.getParameter("deliveryMethod")
+ "</td>");
writer.println("</tr>");
writer.println("<tr>");
writer.println("<td>Catalog Request:</td>");
writer.println("<td>");
if (req.getParameter("catalogRequest") == null) {
writer.println("No");
} else {
writer.println("Yes");
}
writer.println("</td>");
writer.println("</tr>");
writer.println("</table>");
writer.println("<div style='border:1px solid #ddd;" +
"margin-top:40px;font-size:90%'>");
writer.println("Debug Info<br/>");
Enumeration<String> parameterNames = req
.getParameterNames();
while (parameterNames.hasMoreElements()) {
String paramName = parameterNames.nextElement();
writer.println(paramName + ": ");
String[] paramValues = req
.getParameterValues(paramName);
for (String paramValue : paramValues) {
writer.println(paramValue + "<br/>");
}
}
writer.println("</div>");
writer.println("</body>");
writer.println("</html>");
}
doGet()发送的表单如下:
<form method='post'>
<input name='name'/>
<textarea name='address' cols='40' rows='5'></textarea>
<select name='country'>");
<option>United States</option>
<option>Canada</option>
</select>
<input type='radio' name='deliveryMethod' value='First Class'/>
<input type='radio' name='deliveryMethod' value='Second Class'/
> <
textarea name='instruction' cols='40' rows='5'></textarea>
<textarea name='instruction' cols='40' rows='5'></textarea>
<input type='checkbox' name='catalogRequest'/>
<input type='reset'/>
<input type='submit'/>
</form>
表单的方法设为post,确保当用户提交表单时,使用HTTP POST方法。它的action属性默认,表示该表单会被提交给请求它时用的相同的URL。
运行效果如下:

提交表单显示如下:

1.11 使用部署描述符
部署的一个网页是用一个路径配置Servlet的映射。在这些范例中,是利用WebServlet标注类型,用一个路径映射了一个Servlet。
利用部署描述符是配置Servlet应用程序的另一种方法,部署描述符的详情将在第13章“部署描述符”中探讨。部署描述符总是命名为web.xml,并且放在WEB-INF目录下。
本章将描述如何创建一个Servlet应用,并为它创建一个web.xml
在本节的示例程序有SimpleServlet和WelcomeServlet两个Servlet,还有一个要映射Servlets的部署描述符。如下:
SimpleServlet.java
public class SimpleServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req,
HttpServletResponse resp)
throws IOException {
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
writer.println("<html><head></head>" +
"<body>Simple Servlet</body></html>");
}
}
WelcomeServlet.java
public class WelcomeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html");
PrintWriter writer = resp.getWriter();
writer.print("<html><head></head>" +
"<body>Welcome</body></html>");
}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>SimpleServlet</servlet-name>
<servlet-class>top.ninwoo.ch1_11.SimpleServlet</servlet-class>
<load-on-startup>10</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SimpleServlet</servlet-name>
<url-pattern>/simple</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>WelcomeServlet</servlet-name>
<servlet-class>top.ninwoo.ch1_11.WelcomeServlet</servlet-class>
<load-on-startup>20</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>WelcomeServlet</servlet-name>
<url-pattern>/welcome</url-pattern>
</servlet-mapping>
</web-app>
使用部署描述符有诸多好处:
-
可以将在@WebServlet中没有对等元素的元素,如load-on-startup元素。这个元素使得Servlet在应用程序启动时加载,而不是在第一次调用时加载。如果Servlet的init方法需要花一些时间才能完成的话,使用load-on-startup意味着第一次调用Servlet所花的时间并不比后续的调用长,这项功能就特别有用。
load-on-startup指明了启动顺序,输入必须为一个整数。具体情况如下:
- 如果该元素的值为负数或者没有设置,则容器会当Servlet被请求时再加载。
- 如果值为正整数或者0时,表示容器在应用启动时就加载并初始化这个servlet,值越小,servlet的优先级越高,就越先被加载。值相同时,容器就会自己选择顺序来加载。
-
如果需要修改配置值,如Servlet路径,则不需要重新编译Servlet类。
-
可以将初始参数传给一个Servlet,并且不需要重新编译Servlet类,就可以对它们进行编辑。
-
部署描述符还允许覆盖在Servlet标注中定义的值。
WEB-INF目录结构如下:

运行结果:


关于部署以及部署描述符的更多信息,请参考第13章。
1.12 小结
javax.servlet.Servlet最基本的Servlet接口,为简化其使用引入GenericServlet的Servlet实现类。因为servlet大多数运行在HTTP环境中,进而派生出一个javax.servlet.http.HttpServlet的子类更为有用。三者关系如下图:


浙公网安备 33010602011771号