Web中Servlet简单总结

Web阶段

HTTP协议概述

主要可以理解为:客户端-发起请求=====服务器-响应请求

  • HTTP(Hyper Text Transfer Protocol):超文本传输协议

  • HTTP 协议是基于 TCP/IP 协议的

  • 超文本:比普通文本更加强大

  • 传输协议:客户端和服务器端的通信规则(握手规则)

请求部分

请求行 请求头 请求空行 请求体

请求的方式:get/post

注意:只有 POST 请求方式才有请求体

常用状态码介绍:

状态码说明
200 一切都OK>
302/307 请求重定向(客户端行为,两次请求,地址栏发生改变)
304 请求资源未发生变化,使用缓存
404 请求资源未找到
500 服务器错误

Servlet(重中之重)

servlet介绍:

Servlet 是运行在 Java 服务器端的程序,用于接收和响应来自客户端基于 HTTP 协议的请求

  • 第一:Servlet是一个运行在web服务端的java小程序

  • 第二:它可以用于接收和响应客户端的请求

  • 第三:要想实现Servlet功能,可以实现Servlet接口,继承GenericServlet或者HttpServlet

  • 第四:每次请求都会执行service方法

  • 第五:Servlet还支持配置

servlet的执行流程

Servlet关系视图

Servlet实现方式

共有3种方式(一般只用第三种)

  1. 第一种 实现 Servlet 接口,实现所有的抽象方法。该方式支持最大程度的自定义。

  2. 第二种 继承 GenericServlet 抽象类,必须重写 service 方法,其他方法可选择重写。该方式让我们开发 Servlet 变得简单。但是这种方式和 HTTP 协议无关。

  3. 第三种 继承 HttpServlet 抽象类,需要重写 doGet 和 doPost 方法。该方式表示请求和响应都需要和 HTTP 协议相关。

Servlet的生命周期

总结来说,只要有请求就是Servlet的诞生之日,请求结束服务器停止,Servlet死亡

Servlet的线程安全

和线程安全一样,需要加锁,注意上锁的对象要唯一

package com.itheima.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.io.PrintWriter;

/*
  Servlet线程安全
*/
public class ServletDemo04 extends HttpServlet{
  //1.定义用户名成员变量
  //private String username = null;

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//       String username = null;
      synchronized (this) { //锁需要唯一,Servlet对象就是唯一的,所以用this
          //2.获取用户名
          username = req.getParameter("username");

          try {
              Thread.sleep(3000);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }

          //3.获取输出流对象
          PrintWriter pw = resp.getWriter();

          //4.响应给客户端浏览器
          pw.print("welcome:" + username);

          //5.关流
          pw.close();
      }
  }

  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      doGet(req,resp);
  }
}

Servlet的映射方式

三种:(经常使用第一种)

  1. 第一种 具体名称的方式。访问的资源路径必须和映射配置完全相同

  2. 第二种 / 开头 + 通配符的方式。只要符合目录结构即可,不用考虑结尾是什么

  3. 第三种 通配符 + 固定格式结尾的方式。只要符合固定结尾格式即可,不用考虑前面的路径

注意:优先级问题。越是具体的优先级越高,越是模糊通用的优先级越低。第一种 -> 第二种 -> 第三种

Servlet多映射的使用场景

场景分析:

  • 如果访问的资源路径是 /vip 商品价格打9折

  • 如果访问的资源路径是 /vvip 商品价格打5折

  • 如果访问的资源路径是其他 商品价格不打折

package com.itheima.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/*
  Servlet 多路径映射
*/
public class ServletDemo06 extends HttpServlet {
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 定义一个商品金额
      int money = 1000;

      //2. 获取访问的资源路径
      String name = req.getRequestURI();
      name = name.substring(name.lastIndexOf("/"));
       
      //3. 条件判断
      if("/vip".equals(name)) {
          //如果访问资源路径是/vip 商品价格为9折
          System.out.println("商品原价为:" + money + "。优惠后是:" + (money*0.9));
      } else if("/vvip".equals(name)) {
          //如果访问资源路径是/vvip 商品价格为5折
          System.out.println("商品原价为:" + money + "。优惠后是:" + (money*0.5));
      } else {
          //如果访问资源路径是其他 商品价格原样显示
          System.out.println("商品价格为:" + money);
      }
  }

  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      doGet(req,resp);
  }
}

ServletConfig的使用

  • <servlet>标签中,通过<init-param>标签来配置。有两个子标签。

  • <param-name>:代表初始化参数的 key。

  • <param-value>:代表初始化参数的 value。

常用方法

ServletContext

作用:可以获得应用的全局初始化参数和达到 Servlet 之间的数据共享。

ServletContext图示:

域对象概念

  • 域对象指的是对象有作用域也就是有作用范围

  • 域对象可以实现数据的共享

  • 不同作用范围的域对象,共享数据的能力也不一样

  • 在 Servlet 规范中,一共有 4 个域对象

    ServletContext 就是其中的一个

ServletContext的使用

ServletContext 并不属于某个 Servlet 的配置,而是针对于整个应用的配置,也叫全局的初始化参数

<web-app>标签中,通过<context-param>标签来配置。有两个子标签

<param-name>:代表全局初始化参数的 key

<param-value>:代表全局初始化参数的 value

 

配置Servlet,并且配置ServletContext

<web-app>
  ....
  <!--配置Servlet-->
  <servlet>
      <servlet-name>servletContextDemo</servlet-name>
      <servlet-class>com.itheima.servlet.ServletContextDemo</servlet-class>
  </servlet>
  <servlet-mapping>
      <servlet-name>servletContextDemo</servlet-name>
      <url-pattern>/servletContextDemo</url-pattern>
  </servlet-mapping>


  <!--配置ServletContext-->
  <context-param>
      <param-name>globalEncoding</param-name>
      <param-value>UTF-8</param-value>
  </context-param>
  <context-param>
      <param-name>globalDesc</param-name>
      <param-value>This is ServletContext</param-value>
  </context-param>
</web-app>

常用方法

 //获取ServletContext对象
ServletContext context = getServletContext();
//修改ServletContextDemo:存储数据
//向域对象中存储数据
context.setAttribute("username","zhangsan");
//修改ServletConfigDemo:获取数据
  //获取ServletContextDemo设置共享的数据
  Object username = context.getAttribute("username");
  System.out.println(username);

自动注解开发Servlet

不需要web下的WEB-INF, web.xml也没有了

index.jsp也没用

Servlet应用案例-学生管理系统 ***

案例效果介绍

  • 效果

    • 访问案例首页,看到一个可以保存学生信息的界面

    • 输入内容,点击保存,通过java服务器,然后最终保存到txt中

    • 最后java服务器返回成功结果

案例实现

  1. 创建一个 web 项目:servlet_test,配置虚拟目录/stu

    • 选择javaee 7,这里我们还是用web.xml

  2. 创建一个用于保存学生信息的 html 文件:web下新建addStudent.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
       <meta charset="UTF-8">
       <title>添加学生</title>
    </head>
    <body>
      -- ?username=张三&age=18&score=718
       <form action="/stu/studentServlet" method="get" autocomplete="off">
          学生姓名:<input type="text" name="username"> <br/>
          学生年龄:<input type="number" name="age"> <br/>
          学生成绩:<input type="number" name="score"> <br/>
           <button type="submit">保存</button>
       </form>
    </body>
    </html>
  3. 创建一个类com.itheima.servlet.StudentServlet,继承 HttpServlet

  4. 重写 doGet 和 doPost 方法

    package com.itheima.servlet;

    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.BufferedWriter;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.PrintWriter;

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

       @Override
       protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
           doGet(req,resp);
      }
    }
  5. 在 web.xml 文件中修改默认主页和配置 Servlet

     <!--修改默认主页-->
       <welcome-file-list>
           <welcome-file>/addStudent.html</welcome-file>
       </welcome-file-list>

       <!--配置Servlet-->
       <servlet>
           <servlet-name>studentServlet</servlet-name>
           <servlet-class>com.itheima.servlet.StudentServlet</servlet-class>
       </servlet>
       <servlet-mapping>
           <servlet-name>studentServlet</servlet-name>
           <url-pattern>/studentServlet</url-pattern>
       </servlet-mapping>
  6. 在 doGet 方法中接收表单数据保存到文件中,并响应给浏览器结果

    // -- ?username=张三&age=18&score=718
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
           //获取表单中的数据
           String username = req.getParameter("username"); // 获取url后边的?的参数
           String age = req.getParameter("age");
           String score = req.getParameter("score");

           //将数据保存到stu.txt文件中
           BufferedWriter bw = new BufferedWriter(new FileWriter("d:\stu.txt",true));
           bw.write(username + "," + age + "," + score);
           bw.newLine();
           bw.close();

           //给浏览器回应
           PrintWriter pw = resp.getWriter();
           pw.println("Save Success~");
           pw.close();
      }
  7. 部署并启动项目

  8. 通过浏览器测试

请求对象

请求对象常用方法1-获取各种路径

request.

获取请求参数工具类封装,可以使用apache的beanutils包

 

用流的形式读取请求信息

注意:

获取文字 字符时用getReader(字符流,如果采用字符流必须使用post请求方式)

获取图片 文件时用getInputStream(字节流)

请求正文中文编码问题

post方式请求

//设置编码格式
req.setCharacterEncoding("UTF-8");

get请求方式

//输出到浏览器:注意响应的乱码问题已经解决了
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.write(username);

请求域

可以在一次请求范围内共享数据,一般用于请求,转发的多个资源中 共享数据

 

代码展示

响应对象

  • 响应:回馈结果。在 BS 架构中,就是服务器给客户端浏览器反馈结果

  • 响应对象:就是在项目中用于发送响应的对象

//解决中文乱码
resp.setContentType("text/html;charset=UTF-8");

案例:响应图片

  1. 创建字节输入流对象,关联读取的图片路径

  2. 通过响应对象获取字节输出流对象

  3. 循环读取和写出图片

  4. 案例:新建ServletDemo03

/*
  响应图片到浏览器
*/
@WebServlet("/servletDemo03")
public class ServletDemo03 extends HttpServlet {
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      //1. 创建字节输入流对象,关联图片路径
      String realPath = getServletContext().getRealPath("/img/hm.png");
      System.out.println(realPath);
      BufferedInputStream bis = new BufferedInputStream(new FileInputStream(realPath));
  //注意:不能直接指定图片路径,需要同getRealPath获取,因为项目还要发布,我们需要获取到发布之后的图片路径
      //BufferedInputStream bis = new BufferedInputStream(new FileInputStream(/img/hm.png));
      //2. 获取字节输出流对象
      ServletOutputStream sos = resp.getOutputStream();

      //3. 循环读写
      byte[] arr = new byte[1024];
      int len;
      while((len = bis.read(arr)) != -1) {
          sos.write(arr,0,len);
      }

      bis.close();
      sos.close();
  }

  @Override
  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      doGet(req,resp);
  }
}

设置缓存时间

String news = "这是一条很火爆的新闻~~";

//设置缓存时间:1小时的缓存时间
//参数1:Expires : 失效的意思
//参数2:当前时间+1小时毫秒值(意思就是在1小时之后过期)
resp.setDateHeader("Expires",(System.currentTimeMillis()+1*60*60*1000L));

//设置编码格式
resp.setContentType("text/html;charset=UTF-8");
//写出数据
resp.getWriter().write(news);
System.out.println("aaa");
}

设置定时刷新

 String news = "您的用户名或密码错误,3秒后自动跳转到登录页面...";

      //设置编码格式
      resp.setContentType("text/html;charset=UTF-8");
      //写出数据
      resp.getWriter().write(news);

      //设置响应消息头定时刷新
  //Refresh:刷新
      //第二个参数第一部分:3,3设之后
      //第二个参数第二部分:跳转到哪个路径
      //resp.setHeader("Refresh","3;URL=/response/login.html");
      resp.setHeader("Refresh","3;URL="+req.getContextPath()+"/login.html");
  }

请求重定向

/*
  请求重定向
*/
@WebServlet("/servletDemo06")
public class ServletDemo06 extends HttpServlet {
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      //设置请求域数据
      req.setAttribute("username","zhangsan");

      //设置重定向
      resp.sendRedirect(req.getContextPath() + "/servletDemo07");
  //resp.sendRedirect("/response/servletDemo07");
// resp.sendRedirect("https://www.baidu.com");
  }
/*
  请求重定向
*/
@WebServlet("/servletDemo07")
public class ServletDemo07 extends HttpServlet {
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      System.out.println("servletDemo07执行了...");
      Object username = req.getAttribute("username");//获取不到
      System.out.println(username);
  }

总之一句话:重定向是两次请求,设置的数据不会共享

  • 重定向与转发的区别

  • 重定向:

    • 两次请求

    • 地址栏发生变化

    • 不可以使用request域共享数据 (既然是两次请求,那肯定不能使用请求域中共享的数据)

    • 可以重定向到其他服务器的url

  • 转发:

    • 一次请求

    • 地址栏不发生变化

    • 可以使用request域共享数据

    • 只能转发到自己服务器内部的url

响应对象-文件下载

 //1.创建字节输入流,关联读取的文件
        //获取文件的绝对路径
        String realPath = getServletContext().getRealPath("/img/hm.png");
        //创建字节输出流对象
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(realPath));

        //2.设置响应头支持的类型  应用支持的类型为字节流
        /*
            Content-Type 消息头名称   支持的类型
            application/octet-stream   消息头参数  应用类型为字节流
         */
        resp.setHeader("Content-Type","application/octet-stream");

        //3.设置响应头以下载方式打开  以附件形式处理内容
        /*
            Content-Disposition  消息头名称  处理的形式
            attachment;filename=  消息头参数  附件形式进行处理;指定下载文件名称
         */
        resp.setHeader("Content-Disposition","attachment;filename=hm.png");

        //4.获取字节输出流对象
        ServletOutputStream sos = resp.getOutputStream();

        //5.循环读写文件
        byte[] arr = new byte[1024];
        int len;
        while((len = bis.read(arr)) != -1) {
            sos.write(arr,0,len);
        }

        //6.释放资源
        bis.close();
        //sos.close();
    }

 

原文地址:https://www.cnblogs.com/sunhao410526/p/14308368.html