Servlet

The hurricane seeks the shortest road by the no-road, and suddenly ends its search in the Nowhere.

风于无路之中寻求最短之路,又突然地在“无何有之国”终之了它的追求。

Servlet

Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。

狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。

最早支持Servlet标准的是JavaSoft的Java Web Server,此后,一些其它的基于Java的Web服务器开始支持标准的Servlet。

Servlet原理

Servlet运行过程

Servlet的工作过程

步骤:

  1. Web Client 向Servlet容器(Tomcat)发出Http请求
  2. Servlet容器接收Web Client的请求
  3. Servlet容器创建一个HttpRequest对象,将Web Client请求的信息封装到这个对象中。
  4. Servlet容器创建一个HttpResponse对象
  5. Servlet容器调用HttpServlet对象的doservice方法,把HttpRequest对象与HttpResponse对象作为参数传给HttpServlet 对象。
  6. HttpServlet调用HttpRequest对象的有关方法,获取Http请求信息。
  7. HttpServlet调用HttpResponse对象的有关方法,生成响应数据。
  8. Servlet容器把HttpServlet的响应结果传给Web Client。
  1. 客户端的网络请求首先会被Http服务器接收(也叫Web服务器、web容器,其需要提供web应用运行所需的环境,接收客户端的Http请求);
  2. Web服务器根据请求的路径将请求转交给对应的Servlet容器(也称Servlet引擎,为Servlet的运行提供环境支持,可以理解为tomcat或其他服务器);
  3. Servlet容器根据对应的虚拟路径(@WebServlet中配置的)来加载Servlet,如果Serlvet没有被实例化则创建该Servlet的一个实例(调用init方法);
  4. Servlet容器根据用户的HTTP请求,创建一个ServletRequest对象(HTTP的请求信息被封装在其中)和一个可以对HTTP请求进行响应的ServletResponse对象(类似于寄信,并在信中说明回信的地址),然后调用HttpServlet中重写的service(ServletRequest req, ServletResponse res)方法,并在这个方法中,将ServletRequest、ServletResponse这两个对象向下转型,得到我们非常熟悉的HttpServletRequest和HttpServletResponse两个对象,然后将客户端的请求转发到HttpServlet中protected修饰的service(HttpServletRequest req, HttpServletResponse resp);
  5. service(HttpServletRequest req, HttpServletResponse resp)根据请求的method(get、post、put、delete、head、options、trace)来调用不同的方法,如doGet、doPost;
  6. 服务端处理完Http的请求后,根据HttpServletResponse对象将处理结果作为Http响应返回给客户端。

Servlet UML关系图

img

ServletContext

web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的web应用;

  1. 共享数据

    1
    2
    ServletContext context = this.getServletContext(); 
    context.setAttribute("数据key",数据value); //将一个数据保存
  2. 获取初始化参数

    1
    2
    3
    4
    5
    <!--web.xml--> 
    <!--配置一些web应用初始化参数-->
    <context-param>
    <param-name>url</param-name>
    <param-value>jdbc:mysql://localhost:3306/javaweb</param-value> </context-param>
    1
    2
    3
    4
    5
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
    ServletContext context = this.getServletContext();
    String url = context.getInitParameter("url");
    resp.getWriter().print(url);
    }
  3. 请求转发

    1
    2
    3
    4
    5
    6
    @Override 
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    ServletContext context = this.getServletContext();

    context.getRequestDispatcher("/forwardpath").forward(req,resp);
    }
  4. 读取资源文件

    Properties

    • 在java目录下新建properties

    • 在resources目录下新建properties

      1
      2
      username=root12312 
      password=zxczxczxc

    发现:都被打包到了同一个路径下:classes,我们俗称这个路径为classpath:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Override 
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    InputStream is = this.getServletContext().getResourceAsStream("/WEB- INF/classes/com/bobo/xx.properties");
    Properties prop = new Properties();
    prop.load(is);
    String user = prop.getProperty("username");
    String pwd = prop.getProperty("password");
    resp.getWriter().print(user+":"+pwd);
    }

HttpServletResponse

  1. 简单分类

    负责向浏览器发送数据的方法

    1
    2
    ServletOutputStream getOutputStream() throws IOException;
    PrintWriter getWriter() throws IOException;

    从中我们可以看到,getOutputStream()方法返回ServletOutputStream对象,更适合向客户端写入二进制数据,并且Servlet容器不会对这些二进制数据进行编码,因此我们常用ServletOutputStream来向客户端发送如图片、文件等内容;对于getWriter()方法返回的PrintWriter对象,里面封装了更多的写入字符文本的函数,并且我们上文提到的setContentType()方法设置的MIME类型对其输出内容有效,因此也可以很好地解决中文乱码问题。

    • 还有一点需要注意的是,这两个方法在一个response对象中不可以同时调用,否则会抛出一个IllegalStateException,也就是非法状态异常,因为输出流只能有一个(如果可以多次获取的话,客户端又如何确认哪个Http响应是最后一个呢)。

      点击跳转大佬

    负责向浏览器发送响应头的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    void setCharacterEncoding(String var1);
    void setContentLength(int var1);
    void setContentType(String var1);

    void setDateHeader(String var1, long var2);
    void addDateHeader(String var1, long var2);
    void setHeader(String var1, String var2);
    void addHeader(String var1, String var2);
    void setIntHeader(String var1, int var2);
    void addIntHeader(String var1, int var2);

    相应状态码的常量

    | Name | discribtion | 释义 |
    | :–: | :———————-: | :—————————————: |
    | 200 | SC_OK | 此次请求已经成功 |
    | 301 | SC_MOVED_PERMANENTLY | 请求的网页已永久移动到新位置 |
    | 302 | SC_MOVED_TEMPORARILY | 临时移动、请求地址不变 |
    | 401 | SC_UNAUTHORIZED | 未授权、用户需登录 |
    | 403 | SC_FORBIDDEN | 服务器拒绝了此次请求(权限问题) |
    | 404 | SC_NOT_FOUND | 服务器没找到URI匹配的 |
    | 405 | SC_METHOD_NOT_ALLOWED | 调用的方法不允许使用(get、post不匹配) |
    | 500 | SC_INTERNAL_SERVER_ERROR | 服务器内部发生异常,请求中断 |
    | 502 | SC_BAD_GATEWAY | 网关错误(如Nginx),无法收到服务器的响应 |
    | 504 | SC_GATEWAY_TIMEOUT | 请求超时,在约定时间内没有收到Http响应 |

  2. 常见应用

    1. 向浏览器输出信息

    2. 下载文件

      1. 要获取下载文件的路径
      2. 下载的文件名
      3. 使浏览器可以支持下载 (Content-disposition) 中文文件名用 URLEncoder.encode 编码,否则可能乱码
      4. 获取下载文件输入流
      5. 创建缓冲区
      6. 获取OutputStream对象
      7. 将FileOutputStream流入到buffer缓冲区
      8. 使用OutputStream将缓冲区中的数据输出到客户端
      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
      package com.bobo.servlet;
      import javax.servlet.ServletException;
      import javax.servlet.ServletOutputStream;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import java.io.FileInputStream;
      import java.io.IOException;
      import java.net.URLEncoder;

      public class FileServlet extends HttpServlet {
      @Override
      protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      resp.setCharacterEncoding("utf-8");

      //1.要获取下载文件的路径
      String realPath = "绝对路径";
      System.out.println("下载的文件的路径:" + realPath);
      //2.下载的文件名
      //substring()截取字符串 lastIndexOf()截取/之后的字符串
      String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
      //3.使浏览器可以支持下载 (Content-disposition) attachment:以附件方式下载 中文文件名用 URLEncoder.encode 编码,否则可能乱码
      resp.setHeader("Content-disposition","attachment;filename" + URLEncoder.encode("fileName","utf-8"));
      //4.获取下载文件输入流
      FileInputStream in = new FileInputStream(realPath);
      //5.创建缓冲区
      int len = 0;
      byte[] buffer = new byte[1024];
      //6.获取OutputStream对象
      ServletOutputStream out = resp.getOutputStream();
      //7.将FileOutputStream流入到buffer缓冲区
      //in.read(buffer)
      //8.使用OutputStream将缓冲区中的数据输出到客户端
      while ((len = in.read(buffer))> 0){
      out.write(buffer,0,len);
      }
      in.close();
      out.close();

      }

      @Override
      protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      super.doPost(req, resp);
      }
      }
    3. 验证码功能

      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
      package com.bobo.servlet;
      import javax.imageio.ImageIO;
      import javax.servlet.ServletException;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import java.awt.*;
      import java.awt.image.BufferedImage;
      import java.io.IOException;
      import java.util.Random;

      public class ImageServlet extends HttpServlet {

      @Override
      protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      //如何让浏览器5s自动刷新一次
      resp.setHeader("refresh","3");
      //在内存中创建一个图片
      BufferedImage image = new BufferedImage(80,20,BufferedImage.TYPE_INT_RGB);
      //得到图片
      Graphics2D g = (Graphics2D) image.getGraphics();
      //设置图片背景颜色
      g.setColor(Color.white);
      g.fillRect(0,0,80,20);
      //给图片写数据
      g.setColor(Color.BLUE);
      g.setFont(new Font(null,Font.BOLD,20));

      g.drawString(makeNum(),0,20);

      //告诉浏览器这个请求用图片方式打开
      resp.setContentType("image/png");
      //网站存在缓存 不让浏览器缓存
      resp.setDateHeader("expires",-1);
      resp.setHeader("Cache-Control","no-cache");
      resp.setHeader("pragma","no-cache");

      //将图片写给浏览器
      boolean write = ImageIO.write(image,"png", resp.getOutputStream());
      }

      //生成随机数
      private String makeNum(){
      Random random = new Random();
      //6个9 代表6位数
      String num = random.nextInt(999999) + "";
      StringBuffer sb = new StringBuffer();
      //保证输出6位数
      for (int i = 0; i < 6-num.length() ; i++) {
      sb.append("0");
      }
      String s = sb.toString() + num;
      return num;
      }

      @Override
      protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      super.doPost(req, resp);
      }
      }
    4. 实现重定向

      1
      2
      3
      4
      //resp.setHeader("location","/");
      //resp.setStatus(302);//重定向常量
      //重定向一定要注意路径问题
      resp.sendRedirect("/img"); //重定向

HttpServletRequest

HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,HTTP请求中的所有信息会被封装到HttpServletRequest,通过HttpServletRequest方法,获得客户端的所有信息。

方法 描述
Cookie[] getCookies() 返回一个数组,包含客户端发送该请求的所有的 Cookie 对象。
ServletInputStream getInputStream() 使用 ServletInputStream,以二进制数据形式检索请求的主体。
String getCharacterEncoding() 返回请求主体中使用的字符编码的名称。
String getContentType() 返回请求主体的 MIME 类型,如果不知道类型则返回 null。
String getContextPath() 返回指示请求上下文的请求 URI 部分。
String getHeader(String name) 以字符串形式返回指定的请求头的值。
String getMethod() 返回请求的 HTTP 方法的名称,例如,GET、POST 或 PUT。
String getParameter(String name) 以字符串形式返回请求参数的值,或者如果参数不存在则返回 null。
String getPathInfo() 当请求发出时,返回与客户端发送的 URL 相关的任何额外的路径信息。
String getProtocol() 返回请求协议的名称和版本。
String getQueryString() 返回包含在路径后的请求 URL 中的查询字符串。
String getRemoteAddr() 返回发送请求的客户端的互联网协议(IP)地址。
String getRemoteHost() 返回发送请求的客户端的完全限定名称。
String getRemoteUser() 如果用户已通过身份验证,则返回发出请求的登录用户,或者如果用户未通过身份验证,则返回 null。
String getRequestURI() 从协议名称直到 HTTP 请求的第一行的查询字符串中,返回该请求的 URL 的一部分。
String getRequestedSessionId() 返回由客户端指定的 session 会话 ID。
String getServletPath() 返回调用 JSP 的请求的 URL 的一部分。
int getContentLength() 以字节为单位返回请求主体的长度,并提供输入流,或者如果长度未知则返回 -1。
int getIntHeader(String name) 返回指定的请求头的值为一个 int 值。
int getServerPort() 返回接收到这个请求的端口号。
int getParameterMap() 将参数封装成 Map 类型。
  1. 获取前端参数,请求转发

    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
    package com.bobo.servlet;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.Arrays;

    public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    super.doGet(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //后台接收中文乱码问题
    req.setCharacterEncoding("utf-8");
    resp.setCharacterEncoding("utf-8");

    String username = req.getParameter("username");
    String password = req.getParameter("password");

    System.out.println("============================");

    System.out.println(username);
    System.out.println(password);
    System.out.println("============================");

    //通过请求转发
    // 这里的/代表当前的web应用
    req.getRequestDispatcher("/success.jsp").forward(req,resp);
    }
    }

会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份Session通过在服务器端记录信息确定用户身份

Cookie意为“甜饼”,是由W3C组织提出,最早由Netscape社区发展的一种机制。目前Cookie已经成为标准,所有的主流浏览器如IE、Netscape、Firefox、Opera等都支持Cookie。

  由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理

Session

除了使用Cookie,Web应用程序中还经常使用Session来记录客户端状态。Session是服务器端使用的一种记录客户端状态的机制,使用上比Cookie简单一些,相应的也增加了服务器的存储压力

Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是Session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。

点击跳转Cookie_Session详解
查看评论