zoukankan      html  css  js  c++  java
  • Servlet入门,Servletconfig,Servletcontext,注解配置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)第二种:/开头+通配符的方式

    • 我们还是使用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. 第一次访问时创建

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

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

    1. 服务器加载时创建

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

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

     

    • 修改 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. 通过浏览器测试

       

  • 相关阅读:
    轮播图案例
    如何使用google等一系列搜索引擎?
    开源项目weiciyuan运行前gradle调试过程记录
    onRetainNonConfigurationInstance方法状态保存
    关于stickybroadcast
    关于Bundle对象的思考
    android中finish和system.exit方法退出的区别
    关于RAW 和 ASSEST文件夹的差异
    图片缓存核心类LruCache
    Android常用开源库集合【持续更新】
  • 原文地址:https://www.cnblogs.com/859630097com/p/14300096.html
Copyright © 2011-2022 走看看