《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);
}
  1. 原始的service方法将request和response分别转换为HttpServletRequest和HttpServletResponse,并调用新的service方法.
  2. 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>&nbsp;</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>&nbsp;</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>

使用部署描述符有诸多好处:

  1. 可以将在@WebServlet中没有对等元素的元素,如load-on-startup元素。这个元素使得Servlet在应用程序启动时加载,而不是在第一次调用时加载。如果Servlet的init方法需要花一些时间才能完成的话,使用load-on-startup意味着第一次调用Servlet所花的时间并不比后续的调用长,这项功能就特别有用。

    load-on-startup指明了启动顺序,输入必须为一个整数。具体情况如下:

    1. 如果该元素的值为负数或者没有设置,则容器会当Servlet被请求时再加载。
    2. 如果值为正整数或者0时,表示容器在应用启动时就加载并初始化这个servlet,值越小,servlet的优先级越高,就越先被加载。值相同时,容器就会自己选择顺序来加载。
  2. 如果需要修改配置值,如Servlet路径,则不需要重新编译Servlet类。

  3. 可以将初始参数传给一个Servlet,并且不需要重新编译Servlet类,就可以对它们进行编辑。

  4. 部署描述符还允许覆盖在Servlet标注中定义的值。

WEB-INF目录结构如下:

运行结果:

关于部署以及部署描述符的更多信息,请参考第13章。

1.12 小结

javax.servlet.Servlet最基本的Servlet接口,为简化其使用引入GenericServlet的Servlet实现类。因为servlet大多数运行在HTTP环境中,进而派生出一个javax.servlet.http.HttpServlet的子类更为有用。三者关系如下图:

posted @ 2018-12-08 20:33  NinWoo  阅读(155)  评论(0)    收藏  举报