1.Servlet简介

1.1.Servlet概述

Servlet是用Java语言编写的运行在服务器端的程序,能 够接收Web客户端的请求,并能对 Web客户端进行响应,通常是通过HTTP协议进行工作的。
Servlet的运行需要有 Web容器的支持,它为 Servlet提供了运行时环境,并负责在适当的时刻加载和调用 Servlet。 Servlet的主要作用有:

  1. 读取客户发送的所有数据
  2. 查询 HTTP 请求中包含的任何其他请求信息
  3. 处理数据并生成结果
  4. 设置合适的 HTTP 响应参数
  5. 将响应信息回送给客户端

    1.2.Servlet API结构

    Servlet API中包含两个软件包:

  6. javax.servlet:定义了所有Servlet类都必须实现或继承的通用接口和类。 此包下有一个GenericServlet抽象类。它是一个通用的协议无关的Servlet。 继承自GenericServlet的Servlet应该要覆盖service()方法。

  7. javax.servlet.http包:定义了采用HTTP协议通信的HttpServlet类。 此包下有HttpServlet抽象类。它继承自GenericServlet。能够处理HTTP请求的Servlet, 它在原有Servlet接口上添加了对HTTP协议的处理,它比Servlet接口的功能更为强大。

    注意:由于普遍都采用http协议,所以在实际开发中,我们创建一个Servlet时,继承HttpServlet即可。

2.第一个Servlet程序

2.1.创建Servlet

创建demo工程,在src下创建servlet包,在此包中创建一个HelloServlet类

  1. package servlet;
  2. import java.io.IOException;
  3. import java.io.PrintWriter;
  4. import javax.servlet.ServletException;
  5. import javax.servlet.annotation.WebServlet;
  6. import javax.servlet.http.HttpServlet;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9. @WebServlet(urlPatterns="/hello")
  10. public class HelloServlet extends HttpServlet {
  11. @Override
  12. protected void doGet(HttpServletRequest request, HttpServletResponse response)
  13. throws ServletException, IOException {
  14. //设置请求信息的字符编码(必须在使用request之前进行设置)
  15. request.setCharacterEncoding("utf-8");
  16. //使客户端浏览器并根据不同的MIME调用浏览器内不同的程序嵌入模块来处理相应的数据。
  17. response.setContentType("text/html;charset=utf-8");
  18. //设置响应信息的字符编码(必须在创建响应输出流之前进行设置)
  19. response.setCharacterEncoding("utf-8");
  20. //获取客户端提交参数
  21. String name = request.getParameter("name");
  22. //创建响应输出流
  23. PrintWriter out = response.getWriter();
  24. //向客户端响应信息
  25. out.println("<html>");
  26. out.println(" <head><title>Servlet</title></head>");
  27. out.println(" <body>");
  28. out.println("你好,"+name+",欢迎来到Servlet的世界!");
  29. out.println(" </body>");
  30. out.println("</html>");
  31. //关闭响应输出流
  32. out.close();
  33. }
  34. @Override
  35. protected void doPost(HttpServletRequest request, HttpServletResponse response)
  36. throws ServletException, IOException {
  37. doGet(request, response);
  38. }
  39. }

注意:

  1. 一个类继承HttpServlet类,我们就将它称为一个Servlet。
  2. 继承HttpServlet类后,必须要重写doGet和doPost方法。 当get请求时,自动由doGet方法进行处理。 当post请求时,自动由doPost方法进行处理。
  3. doGet和doPost方法都有两个参数:HttpServletRequest、HttpServletResponse HttpServletRequest:封装了请求信息。 HttpServletResponse:封装了响应信息。
  4. 所以,可以使用HttpServletRequest对象中的getParameter方法获取客户端提交参数。 使用HttpServletResponse创建响应输出流,向客户端响应信息。
  5. @WebServlet注解中,使用urlPatterns属性来设置Servlet映射:将客户端请求映射到对应的Servlet上。
  6. @WebServlet注解中,也可以简写为:@WebServlet(“/hello”)

2.2.访问Servlet

启动Tomcat服务器,然后在浏览器地址栏中输入:http://localhost:8080/demo/hello?name=zhangsan 就可以看到Servlet响应信息了。

3.Servlet生命周期

Servlet实例从创建直到毁灭的整个过程,就是Servlet 生命周期。以下是 Servlet 遵循的过程:

  1. Servlet 容器创建 Servlet 的实例(下面会详述Servlet实例创建时机)
  2. Servlet 通过调用 init () 方法进行初始化(只调用一次)。
  3. Servlet 调用 service() 方法来处理客户端的请求(每一次请求都要调用一次)。
    service() 方法会自动调用doGet或doPost。
  4. Servlet 通过调用 destroy() 方法终止(只调用一次)。

在调用 destroy() 方法之后,servlet 对象被标记为垃圾回收。

3.1.演示Servlet生命周期

  1. @WebServlet("/hello")
  2. public class HelloServlet extends HttpServlet {
  3. @Override
  4. public void init() throws ServletException {
  5. System.out.println("init方法");
  6. }
  7. @Override
  8. protected void service(HttpServletRequest arg0, HttpServletResponse arg1)
  9. throws ServletException, IOException {
  10. PrintWriter out = arg1.getWriter();
  11. out.println("hello!aaaaa");
  12. }
  13. @Override
  14. public void destroy() {
  15. System.out.println("destroy方法");
  16. }
  17. }

3.2.Servlet实例的创建时机

Servlet对象由Web服务器负责创建,创建的时机可以使用loadOnStartup属性进行配置

  1. loadOnStartup属性的值必须是一个整数,表示servlet应该被载入的顺序
  2. 当值为0或者大于0时,表示容器在应用启动时就加载并初始化这个servlet;
  3. 当值小于0或者没有指定时,则表示容器在该servlet被选择时才会去加载。
  4. 正数的值越小,该servlet的优先级越高,应用启动时就越先加载。
  5. 当值相同时,容器就会自己选择顺序来加载。
  6. 取值1,2,3,4,5代表的是优先级,而非启动延迟时间。
    @WebServlet(urlPatterns="/hello",loadOnStartup=1)
    

    3.3.Servlet实例的初始化参数

    我们可以在Servlet创建实例时,给Servlet传一些初始化的参数。
    package servlet;
    import java.io.IOException;
    import java.io.PrintWriter;
    import javax.servlet.ServletConfig;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebInitParam;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    //使用注解给Servlet传初始化参数
    @WebServlet(urlPatterns="/hello",initParams={
     @WebInitParam(name="setChar",value="utf-8")
    })
    public class HelloServlet extends HttpServlet {
     @Override
     protected void doGet(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         //获取ServletConfig
         ServletConfig config = this.getServletConfig();
         //然后通过ServletConfig的getInitParameter方法获取初始化参数
         String encoding = config.getInitParameter("setChar");
         request.setCharacterEncoding(encoding);
         response.setContentType("text/html;charset=utf-8");
         response.setCharacterEncoding(encoding);
         String name = request.getParameter("name");
         PrintWriter out = response.getWriter();
         out.println("<html>");
         out.println("  <head><title>Servlet</title></head>");
         out.println("  <body>");
         out.println("你好," + name + ",欢迎来到Servlet的世界!");
         out.println("  </body>");
         out.println("</html>");
         out.close();
     }
     @Override
     protected void doPost(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
         doGet(request, response);
     }
    }
    

    4.HTTP状态码

    HTTP状态码(英语:HTTP Status Code)是用以表示网页服务器超文本传输协议响应状态的3位数字代码。它由 RFC 2616 规范定义的。所有状态码的第一个数字代表了响应的五种状态之一。
    1xx:消息。这一类型的状态码,代表请求已被接受,需要继续处理。这类响应是临时响应,只包含状态行和某些可选的响应头信息,并以空行结束。
    2xx:成功。代表请求已成功被服务器接收、理解、并接受。比如:
  • 200:请求已成功。出现此状态码是表示正常状态。

3xx:重定向。代表需要客户端采取进一步的操作才能完成请求。通常,这些状态码用来重定向,后续的请求地址(重定向目标)在本次响应的 Location 域中指明。
4xx:请求错误。代表客户端请求发生错误。比如:

  • 404:找不到请求的资源
  • 405:服务器端处理请求方法不被允许。

5xx:服务器错误。代表服务器端出现错误。 比如:

  • 500:服务器端代码错误。
  • 503:服务器异常(通常重启服务器即可解决)。

    5.Servlet返回JSON数据

    现在的Web应用程序,都是采用前后端分离的开发模式。这样就要求前端工程与服务器端工程之间,要进行数据交互。
    02.Servlet开发技术 - 图1
    那么,前端向服务器端请求时,采用AJAX请求。而服务器端向前端返回数据时,采用JSON格式数据。
    Jackson 是当前用的比较广泛的,用来序列化和反序列化 json 的 Java 的开源框架。 Jackson 社 区相对比较活跃,更新速度也比较快, 从 Github 中的统计来看,Jackson 是最流行的 json 解析器之一 。Spring MVC 的默认 json 解析器便是 Jackson。 本项目中,Servlet中使用Jackson将java对象或集合转换为json对象或数组后,返回前端。
    package servlet;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.ArrayList;
    import java.util.List;
    import javax.servlet.ServletConfig;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebInitParam;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import po.User;
    @WebServlet("/hello")
    public class HelloServlet extends HttpServlet {
      @Override
      protected void doGet(HttpServletRequest request, HttpServletResponse response)
              throws ServletException, IOException {
          request.setCharacterEncoding("utf-8");
          response.setContentType("text/html;charset=utf-8");
          response.setCharacterEncoding("utf-8");
          List<User> list = new ArrayList<>();
          list.add(new User(1,"张三",20));
          list.add(new User(2,"李四",21));
          list.add(new User(3,"王五",22));
          PrintWriter out = response.getWriter();
          //使用Jackson的jar包中的ObjectMapper进行java对象与jose数据的转换
          ObjectMapper om = new ObjectMapper();
          //使用writeValueAsString方法将java对象转换为json格式字符串
          out.print(om.writeValueAsString(list));  
          out.close();
      }
      @Override
      protected void doPost(HttpServletRequest request, HttpServletResponse response)
              throws ServletException, IOException {
          doGet(request, response);
      }
    }
    
    在浏览器地址栏中直接进行访问,body中就会接收响应的json字符串。
    02.Servlet开发技术 - 图2

    5.服务器端设置允许跨域访问

    我们知道,浏览器默认不允许AJAX跨域访问;所以,下面代码运行后会出现 ”Access-Control-Allow-Origin“ 错误。
    <!DOCTYPE html>
    <html>
      <head>
          <meta charset="utf-8">
          <title></title>
      </head>
      <body>
          <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
          <script>
              axios.get('http://localhost:8080/demo/hello')
                  .then(function(response) {
                      console.log(response.data);
                  })
                  .catch(function(error) {
                      console.log(error);
                  });
          </script>
      </body>
    </html>
    
    此时就会发现,在浏览器控制台中会出现 “跨域访问” 错误。
    02.Servlet开发技术 - 图3
    在前面的课程中,我们使用 Vue-cli 代理实现了前端的跨域访问设置。 下面我们使用CORS来实现服务器跨域访问的设置。

    5.1.CORS简介

    CORS是H5新增的一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing)。

    跨域只存在于浏览器端,不存在于Node.js/python/ java等其它环境;跨域请求是能够发出去的,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。 CORS可以在服务器端响应时,向浏览器发送 “Access-Control-Allow-Origin” 配置信息,通知浏览器解除跨域限制。如果浏览器支持CORS、并且判断Origin通过的话,就会允许XMLHttpRequest发起跨域请求。 所以,CORS配置要写在服务器端。

5.2.CORS跨域设置

CORS跨域设置:response.setHeader(“Access-Control-Allow-Origin”, “*”);

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //设置允许跨域访问,并且设置允许什么样的url进行跨域访问。
        response.setHeader("Access-Control-Allow-Origin", "*"); 
        //response.setHeader("Access-Control-Allow-Origin", "http://localhost:8081"); 
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        response.setCharacterEncoding("utf-8");
        List<User> list = new ArrayList<>();
        list.add(new User(1,"张三",20));
        list.add(new User(2,"李四",21));
        list.add(new User(3,"王五",22));
        PrintWriter out = response.getWriter();
        ObjectMapper om = new ObjectMapper();
        out.print(om.writeValueAsString(list));  
        out.close();
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

此时使用AJAX访问,就会发现已经允许跨域访问了。

7.请求与响应的总结

7.1.HttpServletRequest常用API

  1. getParameter() 获取客户端提交的单值参数,也就是返回值是一个字符串。
  2. getParameterValues() 获取客户端提交的多值参数(复选框组),也就是返回值是一个字符串数组。
  3. setCharacterEncoding() 设置请求的字符编码。

    7.2.HttpServletResponse常用API

  4. getWriter() 获取一个向客户端响应的输出流。

  5. setCharacterEncoding() 设置输出流的字符编码。
  6. setContentType() 设置MIME类型,也就是通知浏览器使用何种编码解析响应数据。

课后作业

  1. 简述Servlet的创建过程?
  2. 简述HttpServletRequest、HttpServletResponse两个参数的作用?
  3. 简述Servlet 生命周期过程?
  4. 简述@WebServlet注解的作用?
  5. 简述如何配置Servlet的创建时机?
  6. 什么是HTTP状态码?请至少说出4种HTTP状态码及其作用?
  7. 简述CORS的跨域设置?
  8. 编程题:
    1. 使用Servlet实现一个登录案例。
    2. 编写一个用户登录页面,包括用户名和密码表单。
    3. 编写一个LoginServlet,并在Servlet中判断是否登录成功,然后向前端返回成功或失败的json数据。
    4. 前端使用ajax请求,并通过服务器端返回的json数据来显示是否登录成功。