02Servlet-授课

Servlet-授课

1 Servlet

1.1 Servlet概述

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

  • 如果想实现 Servlet 的功能,可以通过实现 javax.servlet.Servlet 接口或者继承它的实现类

  • 核心方法:service(),任何客户端的请求都会经过该方法
    在这里插入图片描述

  • Servlet是SUN公司提供的一套规范,名称就叫Servlet规范,它也是JavaEE规范之一

  • 我们可以像学习Java基础一样,通过API来学习Servlet

  • 这里需要注意的是,在我们之前JDK的API中是没有Servlet规范的相关内容,需要使用JavaEE的API

  • 目前在Oracle官网中的最新版本是JavaEE8,该网址中介绍了JavaEE8的一些新特性

  • 当然,我们可以通过访问官方API,学习和查阅里面的内容

  • 打开官方API网址,在左上部分找到javax.servlet包,在左下部分找到Servlet,如下图显示:

在这里插入图片描述

  • 通过阅读API,我们得到如下信息:

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

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

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

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

  • 第五:Servlet还支持配置

  • 具体请看下图:

在这里插入图片描述

1.2 Servlet入门 ***

1.2.1 Servlet快速入门

  1. 创建一个 WEB 项目

    • servlet_demo1
  2. 创建一个类继承 GenericServlet

    • 包:com.itheima.servlet

    • 类:ServletDemo1

    • GenericServlet介绍:implements Servlet,实现了Servlet里的大部分方法,只有一个service方式是抽象的,我们只需要实现这个方法即可
      在这里插入图片描述

  3. 重写 service 方法

    package com.itheima.servlet;
    
    import javax.servlet.GenericServlet;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import java.io.IOException;
    
    /*
        Servlet快速入门1
     */
    public class ServletDemo01 extends GenericServlet{
    
        @Override  
        public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
            System.out.println("service方法执行了...");
        }
    }
    
    
    • service有两个参数,servletRequest,servletResponse ,这俩分别是处理请求和响应的,后续会详细介绍
  4. 在 web.xml 中配置 Servlet

    <!--配置快速入门1Servlet-->
    <servlet>
        <servlet-name>servletDemo01</servlet-name>
        <servlet-class>com.itheima.servlet.ServletDemo01</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>servletDemo01</servlet-name>
        <url-pattern>/servletDemo01</url-pattern>
    </servlet-mapping>
    
  5. 部署并启动项目:配置项目虚拟路径:/demo1

  6. 通过浏览器测试
    在这里插入图片描述

  7. 分析图:(下图与实际案例有出入,但是我们只需要搞清楚Servlet访问流程即可)
    在这里插入图片描述

1.2.2 Servlet执行过程分析

  1. 我们通过浏览器发送请求,请求首先到达Tomcat服务器
  2. 由服务器解析请求URL,然后在部署的应用列表中找到我们的应用
  3. 接下来,在我们的应用中找应用里的web.xml配置文件
  4. 在web.xml中找到Servlet的配置
  5. 找到后执行service方法,最后由ServletDemo1响应客户浏览器

整个过程如下图所示:
在这里插入图片描述

一句话总结执行过程:

浏览器——>Tomcat服务器——>我们的应用——>应用中的web.xml——>Servlet——>响应浏览器

1.2.3 Servlet关系视图

  • 关系视图如下:
    在这里插入图片描述

    • Servlet,GenericServlet,HTTPServlet,他们都有service方法
    • service方法都有俩参数,ServletRequest,ServletResponse
    • HTTPServlet的参数是HttpServletRequest,HttpServletResponse,他们分别继承自ServletRequest,ServletResponse
    • 这俩参数都是接口,分别处理请求,响应
    • ServletConfig,处理配置
    • ServletContext,处理多个Servlet之间信息共享

1.2.4 Servlet实现方式

1)实现方式说明
  1. 第一种
    实现 Servlet 接口,实现所有的抽象方法。该方式支持最大程度的自定义。
  2. 第二种
    继承 GenericServlet 抽象类,必须重写 service 方法,其他方法可选择重写。该方式让我们开发 Servlet 变得简单。但是这种方式和 HTTP 协议无关。
  3. 第三种
    继承 HttpServlet 抽象类,需要重写 doGet 和 doPost 方法。该方式表示请求和响应都需要和 HTTP 协议相关。
  • 上述前两种都给大家演示过了,我们接下来来试一下第三种
2)继承HttpServlet ***
  • 步骤

    1. 创建一个类继承 HttpServlet

      //servlet_demo1新建:ServletDemo02.java
      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快速入门2
       */
      public class ServletDemo02 extends HttpServlet {
        
      }
      
      
    2. 重写 doGet 和 doPost 方法

      public class ServletDemo02 extends HttpServlet {
      
          @Override
          protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
              System.out.println("方法执行了...");
          }
      
          @Override
          protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
              //post请求方式一般与get请求方式处理的逻辑一样,所以直接调用doGet
              doGet(req,resp);
          }
      }
      
    3. 在 web.xml 中配置 Servlet

      <!--配置快速入门2Servlet-->
      <servlet>
          <servlet-name>servletDemo02</servlet-name>
          <servlet-class>com.itheima.servlet.ServletDemo02</servlet-class>
      </servlet>
      <servlet-mapping>
          <servlet-name>servletDemo02</servlet-name>
          <url-pattern>/servletDemo02</url-pattern>
      </servlet-mapping>
      
    4. 部署并启动项目

    5. 通过浏览器测试
      在这里插入图片描述

1.3 Servlet使用细节

1.3.1 Servlet的生命周期

  • 对象的生命周期,就是对象从出生到死亡的过程。即:出生 -> 活着 -> 死亡。官方说法是对象创建到销毁的过程

  • 出生:请求第一次到达 Servlet 时,对象就创建出来,并且初始化成功。只出生(创建)一次,将对象放到内存中

  • 活着:服务器提供服务的整个过程中,该对象一直存在,每次都是执行 service 方法

  • 死亡:当服务停止时,或者服务器宕机时,对象死亡

  • 结论:Servlet 对象只会创建一次,销毁一次。所以 Servlet 对象只有一个实例。如果一个对象实例在应用中是唯一的存在,那么我们就称它为单例模式

  • 代码

    1. 创建ServletDemo03

      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 ServletDemo03 extends HttpServlet {
          @Override
          public void init() throws ServletException {
              System.out.println("对象创建并初始化了...");
          }
      
          @Override
          protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
              System.out.println("接收到了客户端的请求...");
          }
      
          @Override
          protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
              doGet(req,resp);
          }
      
          @Override
          public void destroy() {
              System.out.println("对象销毁了...");
          }
      }
      
      
    2. 配置Servlet

      <!--演示Servlet生命周期的配置-->
      <servlet>
          <servlet-name>servletDemo03</servlet-name>
          <servlet-class>com.itheima.servlet.ServletDemo03</servlet-class>
      </servlet>
      <servlet-mapping>
          <servlet-name>servletDemo03</servlet-name>
          <url-pattern>/servletDemo03</url-pattern>
      </servlet-mapping>
      
    3. 部署并启动
      在这里插入图片描述

      • 说明:init只会执行一次
    4. 停止服务
      在这里插入图片描述

      • 停止tomcat,执行destory

1.3.2 Servlet的线程安全 **难点

  • 由于 Servlet 采用的是单例模式,也就是整个应用中只有一个实例对象。所以我们需要分析这个唯一的实例对象中的类成员是否线程安全

  • 模拟用户登录功能来查看 Servlet 线程是否安全

  • 结论:一个浏览器代表一个线程,多个浏览器代表多个线程。按理说我们期望的应该是每个浏览器查看的都应该是自己的用户名。而现在的结果是浏览器中数据混乱。因此,我们可以认为 Servlet 是线程不安全的!

  • 解决:定义类成员要谨慎。如果是共用的,并且只会在初始化时赋值,其他时间都是获取的话,那么是没问题的。如果不是共用的,或者每次使用都有可能对其赋值,那就要考虑线程安全问题了,可以将其定义到 doGet 或 doPost 方法内或者使用同步功能即可。

  • 案例演示

    1. 新建ServletDemo4

      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 {
      
                  //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);
          }
      }
      
      
    2. Servlet配置

      <!--演示Servlet线程安全的配置-->
          <servlet>
              <servlet-name>servletDemo04</servlet-name>
              <servlet-class>com.itheima.servlet.ServletDemo04</servlet-class>
          </servlet>
          <servlet-mapping>
              <servlet-name>servletDemo04</servlet-name>
              <url-pattern>/servletDemo04</url-pattern>
          </servlet-mapping>
      
    3. 演示

      • 因为需要演示线程安全,所以需要两个浏览器模拟两个线程,所以要开两个浏览器

      • 谷歌浏览器中url传递参数username=aaa

      • 火狐浏览器中url传递参数username=bbb

      • 然后谷歌浏览器先访问,紧接着火狐浏览器访问
        在这里插入图片描述

      • 结果现象是,俩浏览器都是welcome:bbb

    4. 分析
      在这里插入图片描述

    5. 解决1:将username由成员变量,放到方法中

      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;//谷歌浏览器进来有一个username,火狐进来也有一个username,所以不会覆盖
                  //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);
          }
      }
      
      
    6. 使用同步代码块

      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);
          }
      }
      
      

1.3.3 不同映射方式

1)介绍
  1. 第一种
    具体名称的方式。访问的资源路径必须和映射配置完全相同
  2. 第二种
    / 开头 + 通配符的方式。只要符合目录结构即可,不用考虑结尾是什么
  3. 第三种
    通配符 + 固定格式结尾的方式。只要符合固定结尾格式即可,不用考虑前面的路径

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

2)第一种:具体名称的方式 (精准匹配)***
  • 此种方式,只有和映射配置一模一样时,Servlet才会接收和响应来自客户端的请求。

  • 例如:映射为:/servletDemo5

  • 访问URL:http://localhost:8080/demo1/servletDemo5

  • 新建ServletDemo5

    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 ServletDemo05 extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("ServletDemo05执行了...");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req,resp);
        }
    }
    
    
  • 配置Servlet

     <!--演示Servlet不同映射方式-->
    <!--具体名称的方式-->
    <servlet>
        <servlet-name>servletDemo05</servlet-name>
        <servlet-class>com.itheima.servlet.ServletDemo05</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>servletDemo05</servlet-name>
        <url-pattern>/servletDemo05</url-pattern>
    </servlet-mapping>
    
  • 访问:
    在这里插入图片描述

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

  • 例如:映射为:/servlet/*

  • 访问URL:http://localhost:8080/demo1/servlet/itheima

    ​ http://localhost:8080/demo1/servlet/itcast

  • 这两个URL都可以。因为用的*,表示/servlet/后面的内容是什么都可以。

  • 我们还是使用ServletDemo5,只需要修改配置即可(把上一个具体名称的配置屏蔽掉)

     <!--/开头+通配符的方式-->
    <servlet>
        <servlet-name>servletDemo05</servlet-name>
        <servlet-class>com.itheima.servlet.ServletDemo05</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>servletDemo05</servlet-name>
        <url-pattern>/servlet/*</url-pattern>
    </servlet-mapping>
    
  • 访问
    在这里插入图片描述

4) 第三种:通配符+固定格式结尾
  • 此种方式,只要符合固定结尾格式即可,其前面的访问URI无须关心(注意协议,主机和端口必须正确)

  • 例如:映射为:*.do

  • 访问URL:http://localhost:8080/demo1/aaa.do

    ​ http://localhost:8080/demo1/bbb.do

  • 这两个URL都可以方法。因为都是以.do作为结尾,而前面用*号通配符配置的映射,所有无须关心。

  • 依然使用ServletDemo5,修改配置即可

    <!--通配符+固定格式结尾的方式-->
    <servlet>2
        <servlet-name>servletDemo05</servlet-name>
        <servlet-class>com.itheima.servlet.ServletDemo05</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>servletDemo05</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
    
  • 访问
    在这里插入图片描述

1.3.4 Servlet多映射的使用场景

  • 我们可以给一个 Servlet 配置多个访问映射,从而根据不同的请求路径来实现不同的功能

  • 场景分析:

    • 如果访问的资源路径是 /vip 商品价格打9折
    • 如果访问的资源路径是 /vvip 商品价格打5折
    • 如果访问的资源路径是其他 商品价格不打折
  • 案例:新建ServletDemo6

    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);
        }
    }
    
    
  • 配置Servlet

    <!--演示Servlet多路径映射-->
        <servlet>
            <servlet-name>servletDemo06</servlet-name>
            <servlet-class>com.itheima.servlet.ServletDemo06</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>servletDemo06</servlet-name>
            <url-pattern>/itheima/*</url-pattern>
        </servlet-mapping>
    
  • 访问
    在这里插入图片描述

1.3.5 Servlet创建时机

  1. 第一次访问时创建

优势:减少对服务器内存的浪费。提高了服务器启动的效率

弊端:如果有一些要在应用加载时就做的初始化操作,无法完成
2. 服务器加载时创建

优势:提前创建好对象,提高了首次执行的效率。可以完成一些应用加载时要做的初始化操作

弊端:对服务器内存占用较多,影响了服务器启动的效率

  • 修改 Servlet 创建时机。在<servlet>标签中,添加<load-on-startup>标签。
    在这里插入图片描述

  • 正整数代表服务器加载时创建,值越小、优先级越高。 负整数或不写代表第一次访问时创建

    • <load-on-startup>加载顺序的序号</load-on-startup>
    • 序号为1,就是服务器启动时第一个加载
    • 序号为2,就是服务器启动时第二个加载
  • 如果两个Servlet都要配置为正整数,那么值小的优先级高

  • 配置:修改ServletDemo3的配置,增加load-on-startup

     <!--演示Servlet生命周期的配置-->
        <servlet>
            <servlet-name>servletDemo03</servlet-name>
            <servlet-class>com.itheima.servlet.ServletDemo03</servlet-class>
            <!--配置Servlet启动时机 正整数代表服务器启动时创建,负数或不写代表第一次访问时创建-->
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>servletDemo03</servlet-name>
            <url-pattern>/servletDemo03</url-pattern>
        </servlet-mapping>
    
  • 效果:如果不配置,是在访问ServletDemo3 的时候初始化,如果配置,那就是在启动tomcat的时候初始化

    • 下图是配置后,启动tomcat打印的
      在这里插入图片描述

1.3.6 默认Servlet

  • 默认Servlet是由服务器提供的一个Servlet,它配置在Tomcat的conf目录下的web.xml中。如下图所示:

在这里插入图片描述

  • 它的映射路径是<url-pattern>/<url-pattern>,我们在发送请求时,首先会在我们项目中的 web.xml 中查找映射配置,找到则执行

  • 但是当找不到对应的 Servlet 路径时,就去找默认的 Servlet,由默认 Servlet 处理。所以,一切都是 Servlet。

  • 访问一个不存在的url
    在这里插入图片描述

    • 这个404界面,其实就是tomcat配置的默认的Servlet处理的结果

2 ServletConfig

2.1 ServletConfig介绍

2.1.1 基本概念 ***

  • ServletConfig 是 Servlet 的配置参数对象,在 Servlet 的规范中,允许为每一个 Servlet 都提供一些初始化的配置。所以,每个 Servlet 都有一个自己的 ServletConfig
  • 作用:在 Servlet 的初始化时,把一些配置信息传递给 Servlet

2.1.2 生命周期

  • 生命周期:和 Servlet 相同

  • 由于它是在初始化阶段读取了web.xml中为Servlet准备的初始化配置,并把配置信息传递给Servlet,所以生命周期与Servlet相同

  • 这里需要注意的是,如果Servlet配置了<load-on-startup>1</load-on-startup>,那么ServletConfig也会在应用加载时创建
    在这里插入图片描述

  • ServletConfig的配置信息都是键值对的形式

2.2 ServletConfig的使用

2.2.1 配置方式

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

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

  • <param-value>:代表初始化参数的 value。
    在这里插入图片描述

    • 一个init-param配置一个信息,一个信息由name和value组成
  • 案例

    1. 新建项目:servlet_demo2

    2. src中新建包:com.itheima.servlet

    3. 新建类:ServletConfigDemo

      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;
      
      /*
          ServletConfig的使用
       */
      public class ServletConfigDemo 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);
          }
      }
      
      
    4. 配置Servlet

      <!--配置Servlet-->
      <servlet>
          <servlet-name>servletConfigDemo</servlet-name>
          <servlet-class>com.itheima.servlet.ServletConfigDemo</servlet-class>
          <!--配置ServletConfig初始化参数-->
          <init-param>
               <!--用于获取初始化参数的key-->
              <param-name>encoding</param-name>
              <!--初始化参数的值-->
              <param-value>UTF-8</param-value>
          </init-param>
          <init-param>
              <param-name>desc</param-name>
              <param-value>This is ServletConfig</param-value>
          </init-param>
      </servlet>
      <servlet-mapping>
          <servlet-name>servletConfigDemo</servlet-name>
          <url-pattern>/servletConfigDemo</url-pattern>
      </servlet-mapping>
      

2.2.2 常用方法

  • 常用方法:
    在这里插入图片描述

    • 掌握getInitParameter()方法
  • 代码:接着在ServletConfigDemo中写代码:

    public class ServletConfigDemo extends HttpServlet {
    
        //声明ServletConfig配置对象
        private ServletConfig config;
    
        /*
            通过init方法来为ServletConfig配置对象赋值
         */
        @Override
        public void init(ServletConfig config) throws ServletException {
            this.config = config;
        }
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //根据key获取value
            String encodingValue = config.getInitParameter("encoding");
            System.out.println(encodingValue);
    
            //获取Servlet的名称
            String servletName = config.getServletName();
            System.out.println(servletName);
    
            //获取所有的key
            Enumeration<String> names = config.getInitParameterNames();
            //遍历得到的key
            while(names.hasMoreElements()) {
                //获取每一个key
                String name = names.nextElement();
                //通过key获取value
                String value = config.getInitParameter(name);
                System.out.println("name:" + name + ",value:" + value);
            }
    
            //获取ServletContext对象
            ServletContext context = config.getServletContext();
            System.out.println(context);
    
            //获取ServletContextDemo设置共享的数据 , 这个是在写下个案例的时候添加的
            //Object username = context.getAttribute("username");
            //System.out.println(username);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req,resp);
        }
    }
    
    
  • 部署项目,配置虚拟目录为demo2,启动tomcat

  • 效果
    在这里插入图片描述

3 ServletContext

3.1 ServletContext介绍

3.1.1 基本介绍 ***

  • ServletContext 是应用上下文对象每一个应用中只有一个 ServletContext 对象

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

  • 上下文理解:环境,不同环境给我们带来的信息是不一样的。所以环境中有很多信息,数据,也就是环境是用于存储数据的。

  • 生命周期:应用一加载则创建,应用被停止则销毁。

    • 出生——活着——死亡
    • 出生: 应用一加载,该对象就被创建出来了。一个应用只有一个实例对象。(Servlet和ServletContext都是单例的)
    • 活着:只要应用一直提供服务,该对象就一直存在。
    • 死亡:应用被卸载(或者服务器挂了),该对象消亡。
  • ServletContext图示:
    在这里插入图片描述

3.1.2 域对象概念 ***

  • 域对象指的是对象有作用域也就是有作用范围
  • 域对象可以实现数据的共享
  • 不同作用范围的域对象,共享数据的能力也不一样
  • 在 Servlet 规范中,一共有 4 个域对象
    • ServletContext 就是其中的一个
    • 它也是 web 应用中最大的作用域,也叫 application 域
      • 在整个项目范围都可以使用 应用域共享的数据
    • 它可以实现整个应用之内的数据共享
    • ServletContext是一个接口,程序运行起来之后打印ServletContext的实例对象,其实是一个ApplicationContextFacade对象
      • ApplicationContextFacade是ServletContext的实现类

3.2 ServletContext的使用

3.2.1 配置方式

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

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

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

  • <param-value>:代表全局初始化参数的 value
    在这里插入图片描述

  • 案例:新建ServletContextDemo

    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;
    
    public class ServletContextDemo 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);
        }
    }
    
    
  • 配置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的配置是在wep-app节点下,与servlet配置同级别

3.2.2 常用方法1 ***

  • 常用方法
    在这里插入图片描述

    • 掌握:getContextPath和getRealPath
  • 准备工作:新建三个空的txt文件,如下
    在这里插入图片描述

  • 代码:继续在ServletContextDemo中写:

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //获取ServletContext对象
            ServletContext context = getServletContext();
    
            //获取全局配置的globalEncoding
            String value = context.getInitParameter("globalDesc");
            System.out.println(value);
    
            //获取应用的访问虚拟目录
            String contextPath = context.getContextPath();
            System.out.println(contextPath);
    
            //根据虚拟目录获取应用部署的磁盘绝对路径
        	String realPath = context.getRealPath("/");
            System.out.println(realPath);
            //获取b.txt文件的绝对路径
            String b = context.getRealPath("/b.txt");
            System.out.println(b);
    
            //获取c.txt文件的绝对路径
            String c = context.getRealPath("/WEB-INF/c.txt");
            System.out.println(c);
    
            //获取a.txt文件的绝对路径
            String a = context.getRealPath("/WEB-INF/classes/a.txt");
            System.out.println(a);
        }
    
    
  • 效果
    在这里插入图片描述

    • context.getRealPath("/");获取到的就是当前项目发布的路径

3.2.3 常用方法2 ***

  • 常用方法2
    在这里插入图片描述

  • 代码:

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

    • 先访问contextdemo存储数据
    • 再访问configdemo获取数据
      在这里插入图片描述

4 注解开发Servlet

4.1 Servlet3.0规范

  • 我们使用的是 Tomcat 9 版本。JavaEE 规范要求是 8 。对应的 Servlet 版本应该是 4.x 版本。但是,在企业开发中,稳定要远比追求新版本要重要。所以我们会降版本使用,用的是 Servlet 3.1 版本

  • 其实我们之前的操作全都是基于 Servlet 2.5 版本规范的,也就是借助于配置文件的方式。后来随着软件开发逐步的演变,基于注解的配置开始流行。而 Servlet 3.0 版本也就开始支持注解开发了

  • Servlet 3.0 版本既保留了 2.5 版本的配置方式,同时又支持了全新的注解配置方式。它可以完全不需要 web.xml 配置文件,就能实现 Servlet 的配置,同时还有一些其他的新特性,我们在后面的课程中会慢慢学习到
    在这里插入图片描述

  • 总结:

    • 之前基于配置文件方式,这个是Servlet2.5版本的规范
    • 但是每添加一个Servlet,就需要自己配置一个,感觉有点繁琐
    • Servlet3.0就出现了注解方式,可以省去配置

4.2 注解开发

4.2.1 自动注解开发Servlet ***

  1. 新建项目:servlet_demo3

  2. 配置Java EE8
    在这里插入图片描述

    • 剩余两步省略
  3. 新建之后,项目目录如下
    在这里插入图片描述

    • web下没有WEB-INF了,web.xml也没有了
      • 虽然web.xml不用了,但是WEB-INF还需要,所以WEB-INF需要自己创建出来
    • index.jsp没用,删除即可
  4. 新建类:com.itheima.servlet.ServletDemo1

    package com.itheima.servlet;
    
    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;
    
    /*
        自动注解配置Servlet
        @WebServlet("Servlet路径")
     */
    @WebServlet("/servletDemo1")
    public class ServletDemo1 extends HttpServlet{
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("servlet执行了...");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req,resp);
        }
    }
    
    
  5. 可以正常访问

4.2.2 注解详解

  • 注解详解
    在这里插入图片描述

4.2.3 手动创建容器(了解)

1)前置说明
  • Servlet 3.0 规范除了使用自动注解的配置方式外,还支持手动创建 Servlet 容器的方式

  • 如果使用必须遵循其编写规范。在 3.0 版本加入了一个新的接口:
    在这里插入图片描述

2)编写步骤
  1. 定义一个类ServletDemo2,继承 HttpServlet

  2. 重写 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.IOException;
    /*
        手动创建容器配置Servlet
     */
    public class ServletDemo2 extends HttpServlet {
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            System.out.println("servlet执行了...");
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            doGet(req,resp);
        }
    }
    
  3. 定义一个类,实现 ServletContainerInitializer 接口

    package com.itheima.servlet;
    
    import javax.servlet.ServletContainerInitializer;
    import javax.servlet.ServletContext;
    import javax.servlet.ServletRegistration;
    import java.util.Set;
    /*
        注册配置Servlet的功能类
     */
    public class MyRegister implements ServletContainerInitializer {
        @Override
        public void onStartup(Set<Class<?>> set, ServletContext servletContext) {
            //完成Servlet的创建和配置
        }
    }
    
    
  4. 在 src 目录下创建一个 META-INF 的

  5. 在 META-INF 包下创建一个 services 的包

  6. 在 services 包下创建一个 javax.servlet.ServletContainerInitializer 的文件

  7. 文件中的内容为容器实现类的全类名

    com.itheima.servlet.MyRegister
    
  8. 在容器实现类中的 onStartup 方法中完成注册 Servlet

    public void onStartup(Set<Class<?>> set, ServletContext servletContext) {
        //完成Servlet的创建和配置
        //1.创建Servlet对象
        ServletDemo2 servletDemo2 = new ServletDemo2();
    
        //2.在ServletContext中添加Servlet,并得到Servlet的配置对象
        ServletRegistration.Dynamic registration = servletContext.addServlet("servletDemo2", servletDemo2);
    
        //3.配置Servlet
        registration.setLoadOnStartup(0);   //Servlet加载时机
        registration.addMapping("/servletDemo2");   //映射访问资源路径
    }
    
  9. 部署并启动项目

  10. 通过浏览器测试
    在这里插入图片描述

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

5.1 案例效果介绍

  • 效果
    在这里插入图片描述

    • 访问案例首页,看到一个可以保存学生信息的界面
    • 输入内容,点击保存,通过java服务器,然后最终保存到txt中
    • 最后java服务器返回成功结果

5.2 案例实现

  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. 通过浏览器测试
    在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

InLoadwetrust

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值