Servlet

前言

  Servlet是开发Servlet的主要技术。掌握Servlet API是成为一名强大的Java Web开发者的基本条件。

Servlet API

  Servlet API有以下4个包:

javax.servlet,其中包含定义Servlet和Servlet容器之间契约的类和接口。
javax.servlet.http,其中包含定义HTTP Servlet和Servlet容器之间契约的类和接口。
java.servlet.annotation
java.servlet.descriptor

   javax.servlet中的主要类型:

Servlet_第1张图片

  Servlet技术的核心是Servlet,它是所有Servlet类必须直接或间接实现的一个接口。在编写实现Servlet的Servlet类时,直接实现它。在扩展实现这个接口的类时,间接实现它。
  Servlet接口定义了Servlet与Servlet容器之间的契约。这个契约归结起来就是,Servlet容器将Servlet类载入内存,并在Servlet实例上调用具体的方法。在一个应用程序中,每种Servlet类型只能有一个实例。
  用户请求使Servlet容器调用Servlet的Service方法,并传入一个ServletRequest实例和一个ServletResponse实例。ServletRequest中封装了当前的HTTP请求,因此,Servlet开发人员不必解析和操作原始的HTTP数据。ServletResponse表示当前用户的HTTP响应,使得将响应发回给用户变得十分容易。
  对于每一个应用程序,Servlet容器还会创建一个ServletContext实例。这个对象中封装了上下文(应用程序)的环境详情。每个上下文只有一个ServletContext。每个Servlet实例也都有一个封装Servlet配置的ServletConfig。

Servlet

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

  • void init(ServletConfig config) throws ServletException
  • void service(ServletRequest request, ServletReponse response) throws ServletException, java.io.IOException
  • void destroy()
  • java.lang.String getServletInfo()
  • ServletConfig getServletConfig()

注意:编写Java方法签名的惯例是,对于与包含该方法的类型不处于同一个包中的类型,要使用全类名。

init、service和destroy是生命周期方法。Servlet容器根据以下规则调用这些方法。

  • init:第一次请求Servlet时,Servlet容器会第一时间调用这个方法。后续请求将不再被调用。
  • service:每次请求Servlet都会调用这个方法。
  • destroy:当要销毁Sevlet时,Servlet容器会调用这个方法。何时销毁?卸载程序或关闭Servlet容器时销毁。

另外两个方法是非生命周期方法。

  • getServletInfo:可以返回有用或为null的任意字符串。
  • getServletConfig:返回由Servlet容器传给init方法的ServletConfig。init方法的ServletConfig必须赋值给一个类级变量。

编写Servlet程序

  • 新建文件MyServlet.java,代码如下:
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * Created by Administrator on 2018/7/30.
 */
@WebServlet(urlPatterns = "/my")
public class MyServlet implements javax.servlet.Servlet {
    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(
                "Hello from " + servletName + ""
);
}
@Override
public String getServletInfo() {
return "My Servlet!";
}
@Override
public void destroy() {
}
}
  • 编译。会报错: 程序包javax.servlet不存在。解决方案:从tomcat的lib目录下找到servlet-api.jar,将其复制到JDK目录jdk1.8.0_60\jre\lib\ext,重新编译。(文件编码问题自行解决)
  • 程序目录。新建目录servlet/WEB-INF/classes,将编译后的.class文件放到classes目录下。
  • 部署。将servlet文件夹放到webapps目录下。
  • 调用Servlet。启动Tomcat,访问地址:http://localhost:8080/servlet/my。
  • 说明:@WebServlet(urlPatterns = “/my”),这个注解相当于web.xml文件中的映射。
  • 访问结果:
    这里写图片描述

ServletRequest

  对于每一个HTTP请求,Servlet容器都会创建一个ServletRequest实例,并将它传给Servlet的Service方法。
  getParameter是在ServletRequest中最常用的方法。该方法通常用于返回HTML表单域的值。示例如下:

//uri
http://domain/context/servletName?id=123

//getParameter
String id = request.getParameter("id");  //如果参数不存在,则返回null

ServletResponse

  Servlet容器在调用Service方法前,会创建一个ServletResponse,并将它作为第二个参数传给Service方法。
  ServletResponse隐藏了向浏览器发送响应的复杂过程。
  在ServletResponse中定义的方法之一是getWriter方法,它返回了一个可以向客户端发送文本的java.io.PrintWriter。
  默认情况下,PrintWriter对象使用ISO-8859-1编码。
  在向客户端发送响应时,大多数时候是将它作为HTML发送。因此,你必须非常熟悉HTML。
  在发送任务HTML标签前,应该先调用setContentType方法,设置响应的内容类型为“text/html”。

ServletConfig

  当容器初始化Servlet时,Servlet容器会给Servlet的init方法传入一个ServletConfig。
  ServletConfig封装了@WebServlet或者部署描述符(web.xml)中的配置信息。通过这种方式传入的每一条信息都叫一个初始参数。一个初始参数有key和value两个元件。
  为了从Servlet内部获取到初始参数的值,ServletConfig提供了几个方法:

//1.获取一个初始参数值
java.lang.String getInitParameter(java.lang.String name)
//2.获取所有初始参数名称的一个Enumeration
java.util.Enumeration.lang.String> getInitParameterNames()

  ServletConfig还提供了getServletContext方法,可以从Servlet内部获取ServletContext。
  非完整示例代码:

@WebServlet(name = "MyServlet",
        urlPatterns = {"/my"},
        initParams = {
                @WebInitParam(name = "admin", value = "mh"),
                @WebInitParam(name = "email", value = "[email protected]")
        }
)
public class MyServlet implements javax.servlet.Servlet {
    ....
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) 
                    throws ServletException, IOException {
        ServletConfig servletConfig = getServletConfig();
        String admin = servletConfig.getInitParameter("admin");
        String email = servletConfig.getInitParameter("email");
    }
    ....
}

  另一种方法是,在部署描述符中传入初始参数。在这里使用部署描述符,比使用@WebServlet更容易,因为部署描述符是一个文本文件,不需要重新编译Servlet类,就可以对它进行编辑。

ServletContext

  ServletContext表示Servlet应用程序。每个Web应用程序只有一个上下文。在将一个应用程序同时部署到多个容器的分布式环境中,每台Java虚拟机只有一个ServletContext对象。
  通过在ServletConfig中调用getServletContext方法,可以获得ServletContext。

GenericServlet

  前面的例子展示了如何通过实现Servlet接口来编写Servlet。但你注意到没有,它们必须给Servlet接口中的所有方法都提供实现,即便其中有一些方法根本不需要包含任何代码。此外,还需要将ServletConfig对象保存到类级变量中。
  为了使代码更加简洁,GenericServlet抽象类出现了,它实现了GenericServlet接口,并完成以下任务:

  • 将init方法中的ServletConfig赋给一个类级变量,以便可以通过调用getServletConfig获取。
  • 为Servlet接口中的所有方法提供默认的实现。

    示例:

@WebServlet(name = "MyGenericServlet",
        urlPatterns = {"/myGeneric"},
        initParams = {
                @WebInitParam(name = "admin", value = "mh"),
                @WebInitParam(name = "email", value = "[email protected]")
        }
)
public class MyGenericServlet extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) 
                            throws ServletException, IOException {
        ServletConfig servletConfig = getServletConfig();
        String admin = servletConfig.getInitParameter("admin");
        String email = servletConfig.getInitParameter("email");
        PrintWriter writer = servletResponse.getWriter();
        writer.print(
                "Admin: " + admin +
                        "
Eamil:"
+ email + "" ); } }

  MyGenericServlet扩展了GenericServlet,而不是实现了Servlet接口,使代码更加简洁。
  通过URL调用MyGenericServlet:http://localhost:8080/servlet/myGeneric,结果如下:

这里写图片描述

  即使GenericServlet是对Servlet一个很好的加强,但它也不常用,因为它毕竟不像HttpServlet那么高级。HttpServlet才是主角,在现实的应用中被广泛应用。

HttpServlet

  javax.servlet.http中包含了用于编写Servlet应用程序的类和接口。其中许多类型覆盖了javax.servlet中的类型。
  下图展示了javax.servlet.http中的主要类型:

Servlet_第2张图片

HttpServlet覆盖GenericServlet中的Service方法,并通过下列签名再添加一个Service方法:

protected void service(HttpServletRequest req, HttpServletResponse resp) 
                                             throws ServletException, IOException 

  像往常一样,Servlet容器调用javax.servlet.Servlet中原始的Service方法。HttpServlet中的实现如下:

public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
    HttpServletRequest request;
    HttpServletResponse response;
    try {
        request = (HttpServletRequest)req;
        response = (HttpServletResponse)res;
    } catch (ClassCastException var6) {
        throw new ServletException("non-HTTP request or response");
    }

    this.service(request, response);
}

  HttpServlet中新的service方法会检验用来发送请求的HTTP方法(通过调用request.getMethod),并调用以下方法之一:doGet、doPost、doHead、doPut、doTrace、doOptions和doDelete。这7种方法中每一种方法都表示一个HTTP方法。doGet和doPost是最常用的。因此,不再需要覆盖Service方法了,只要覆盖doGet或者doPost,或者覆盖doGet和doPost即可。总之,HttpServlet有两个特性在GenericServlet中尚未体现出来。

  • 不覆盖service方法,而是覆盖doGet或doPost,或两者都覆盖。在少数情况下还会覆盖其他5种方法。
  • 使用HttpServletRequest和HttpServletResponse而不是ServletRequest和ServletResponse。

HttpServletRequest表示HTTP环境下的Servlet请求,它扩展了ServletRequest接口,并添加了几个方法,如下:

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

HttpServletResponse表示HTTP环境中的Servlet响应。下面是其中的部分方法:

//给这个响应对象添加一个cookie
void addCookie(Cookie var1);
//给这个响应对象添加一个header
void addHeader(String var1, String var2);
//发送一条响应码,将浏览器跳转到指定的位置
void sendRedirect(String var1) throws IOException;

处理HTML表单

  一个Web应用程序中几乎总会包含一个或者多个HTML表单,供用户输入值。你可以轻松地将一个HTML表单从一个Servlet发送到浏览器。当用户提交表单时,在表单元素中输入的值就会被当作请求参数发送到服务器。
  HTML输入域的值会被当作字符串发送到服务器。空的输入域对应空的字符串。
  HTML的select元素会发送一个字符串。包含多个值的select元素发送一个字符串数组。
  单选框将被选中按钮的值发送到服务器。如果没有选择按钮,不发送任何值,getParameter(fieldName)返回null。
  如果一个表单中包含多个输入同名的元素,那么所有值都会被提交,并且必须利用request.getParameterValues来获取它们。getParameter将只返回最后一个值。
  示例代码:

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * Created by Administrator on 2018/7/30.
 */
@WebServlet(name = "MyHttpServlet",urlPatterns = "/myHttpServlet")
public class MyHttpServlet extends HttpServlet {
    private static final long serialVersionUID = 54L;

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
                        throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter();
        writer.print("");
        writer.print("");
        writer.print("name:"+request.getParameter("name")+"
"
); writer.print("pwd:"+request.getParameter("pwd")); writer.print(""); writer.print(""); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter writer = response.getWriter(); writer.print(""); writer.print(""); writer.print("
"); writer.print("
"
); writer.print("
"
); writer.print(""); writer.print(""
); writer.print(""); writer.print(""); } }

  访问地址:http://localhost:8080/servlet/myHttpServlet
  
  响应结果:

Servlet_第3张图片

  填写表单:
Servlet_第4张图片

  提交结果:
这里写图片描述

  说明:表单的方法设为post,确保当用户提交表单时,使用HTTP POST方法。它的action属性缺省,表示该表单会被提交给请求它时用的相同的URL。

使用部署描述符

  部署描述符总是命名为web.xml,并且放在WEB-INF目录下。可以在其中配置Servlet的映射。示例如下:

public class MyHttpServlet extends HttpServlet {
    private static final long serialVersionUID = 5432L;

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
                        throws ServletException, IOException {
        ......
    }
}

<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_3_1.xsd"
         version="3.1">

    <servlet>
        <servlet-name>MyHttpServletservlet-name>
        <servlet-class>MyHttpServletservlet-class>
    servlet>

    <servlet-mapping>
        <servlet-name>MyHttpServletservlet-name>
        <url-pattern>/myHttpServleturl-pattern>
    servlet-mapping>

web-app>

  使用部署描述符的好处: 如需修改配置项,如Servlet的路径/添加初始参数,不需要重新编译类。
  如果@WebServlet中的定义与部署描述符中的定义冲突,则以部署描述符定义为准。

你可能感兴趣的:(Java)