欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Servlet

程序员文章站 2022-10-01 12:38:04
1.servlet是什么? 是运行在web服务器或应用服务器上的程序,是作为来自web浏览器或其他的http客户端的请求和http服务器上的数据库或应用程序之间的中间层 2.能做什么? 获取表单的信息数据,动态的创建网页,接收请求,响应数据 3.servlet的优点: 1.性能好 2.servlet ......

Servlet相关概念

Servlet快速入门

Servlet相关知识点

ServletConfig

ServletContext

注解开发Servlet

1. Servlet相关概念

Servlet就是运行在服务器端的Java程序,该程序可以接受浏览器请求,给出响应。

  1. Servlet规范是13大规范之一,本质就是一个Servlet接口
  2. JavaEE也有API文档,用法与之前的JavaSE一致。
  3. Servlet是一个接口,有两个常用实现类GenericServletHttpServlet
  4. 通过接口的概述了解到,实现Servlet的三种方式:
    • 实现Servlet接口
    • 继承GenericServlet抽象类
    • 继承HttPServlet
  5. 概述中描述Servlet生命周期:
    • initServlet对象创建的时候调用
    • service:客户端浏览器的所有请求,都会交由该方法处理。处理请求,给出响应。核心方法,很重要。
    • destroyServlet对象销毁时调用
  6. 接口提供了getServletConfiggetServletInfo方法获取配置信息、作者版权等信息
  7. 官方API截图如下:

Servlet

Servlet

2. Servlet快速入门

相关知识:

crm customer relationship manage 客户关系 管理 系统(传统的项目)

2.1 方式一:实现Servlet接口

实现步骤

  1. 创建一个web项目

  2. src下编写一个类StudentServlet,实现Servlet接口

  3. 重点实现service方法

    /*
        Servlet快速入门
     */
    public class StudentServlet implements Servlet{
        /*
            客户端所有请求都会经过service方法
         */
        @Override
        public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
            System.out.println("这是我的第一个servlet入门案例...");
        }
        
        /* 其他方法空实现即可 */
        @Override
        public void init(ServletConfig servletConfig) throws ServletException { }
    
        @Override
        public ServletConfig getServletConfig() {  return null; }
    
        @Override
        public String getServletInfo() {  return null; }
    
        @Override
        public void destroy() { }
    }
    
    
  4. 在模块的web.xml中配置servletmappingServlet映射关系

    <!-- 声明一个servlet -->
    <servlet>
        <!--  serlvetName 一般是类名首字母小写
            先写servlet-class,敲类名,等提示即可
            再写servlet-name  有提示,遵守规范
         -->
        <servlet-name>studentServlet</servlet-name>
        <servlet-class>com.itheima.servlet.StudentServlet</servlet-class>
    </servlet>
    
    <!-- servlet映射 -->
    <servlet-mapping>
        <!--
            通过相同的servlet-name,可以绑定servlet标签和servlet-mapping标签
                url-pattern属性,配置的当前servlet处理的请求的url后缀
         -->
        <servlet-name>studentServlet</servlet-name>
        <url-pattern>/studentServlet</url-pattern>
    </servlet-mapping>
    
  5. 部署项目并启动tomcat

  6. 浏览器访问web.xml中配置的映射路径http://localhost:8080/crm/studentServlet

2.2 方式二:继承GenericServlet抽象类

实现Servlet接口,需要实现包含service方法在内的5个方法,但是其他方法不是我们重点关注的方法。

这个时候可以在当前实现类和Servlet接口之间添加一个中间的抽象类,该类实现除service方法之外的所有方法,我们实现Servlet的时候,就继承该类,只需要实现Service方法即可。

模板方法模式:

Servlet

实现步骤

  1. 创建一个web项目

  2. src下编写一个类ServletDemo01,继承GenericServlet接口

  3. 重点实现service方法

    /*
        Servlet快速入门1
     */
    public class ServletDemo01 extends GenericServlet{
    
        @Override
        public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
            System.out.println("service方法执行了...");
        }
    }
    
  4. 在模块的web.xml中配置servletmappingServlet映射关系

    <!--配置快速入门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. 部署项目并启动tomcat

  6. 浏览器访问web.xml中配置的映射路径http://localhost:8080/servletDemo01

2.3 方式三:继承HttpServlet

实现步骤

  1. 创建一个web项目

  2. src下编写一个类ServletDemo02,继承HttpServlet接口

  3. 重点实现doGet/doPost方法

    /*
        Servlet快速入门2
     */
    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 {
            System.out.println("xxxx = ");
        }
    }
    
  4. 在模块的web.xml中配置servletmappingServlet映射关系

    <!--配置快速入门1Servlet-->
    <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>
    
  5. 部署项目并启动tomcat

  6. 浏览器访问web.xml中配置的映射路径http://localhost:8080/servletDemo02

Servlet

2.4 执行流程

Servlet

2.5 Servlet执行流程

时序图,统一建模语言(UML)的一种,演示的就是某个项目(模块)的执行流程。

Servlet

2.6 Servlet继承体系

类图,统一建模语言(UML)的一种,演示的就是类的继承体系。

Servlet

朴素类图的样子

Servlet

3. Servlet生命周期

对象的生命周期,就是对象从出生到死亡的过程。

即:出生 -> 活着 -> 死亡。专业的说法是对象创建到销毁的过程!

创建:(默认)第一次请求Servlet 时,才会创建并对象并成功初始化,初始化时调用init方法。整个项目运行期间,Servlet对象只创建一次。

运行:该项目提供服务的整个过程中,该对象一直存在。每次请求都会调用对应Servlet的service方法处理。

销毁:当项目应用被卸载时,对象被销毁。

3.1 结论:

Servlet 对象在整个项目运行期间只会创建一次,销毁一次。

Servlet 对象是单例的,且默认为懒汉式单例。

单例:在程序运行期间,某个类的实例有且只能有一个,这种情况就叫做单例。

3.2 演示代码

/*
    Servlet生命周期
 */
public class ServletDemo03 extends HttpServlet {

    private double money;


    /**
     * doGet&doPost方法会被调用多次,每次请求调用一次
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @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);
    }

    /**
     * servlet初始化时调用该方法,默认只在第一次访问该Servlet时才初始化Servlet,并调用一次该方法
     *
     * @throws ServletException
     */
    @Override
    public void init() throws ServletException {
        System.out.println("对象创建并初始化了...");
    }

    /**
     * 项目卸载时,调用该方法
     */
    @Override
    public void destroy() {
        System.out.println("对象销毁了...");
    }
}

控制台输出

[2020-11-20 09:40:11,912] Artifact day07_01_servlet_demo1:war exploded2: Deploy took 201 milliseconds
对象创建并初始化了...
接收到了客户端的请求...
接收到了客户端的请求...
接收到了客户端的请求...
接收到了客户端的请求...
接收到了客户端的请求...
接收到了客户端的请求...
[2020-11-20 09:40:50,001] Artifact day07_01_servlet_demo1:war exploded2: Artifact is being deployed, please wait...
对象销毁了...

4. Servlet 线程安全问题

4.1 问题描述

在多线程环境下,对单例对象的共享数据进行写的操作,就会有线程安全问题。

Servlet默认是单例的,如果存在成员变量,这里的成员变量就是共享数据,因为所有访问该Servlet的用户都可以操作该数据。

一个浏览器代表一个线程,可以使用多个浏览器模拟多个线程。

通过浏览发现多个用户的数据是混乱的,所以单例模式的Servlet 是线程不安全的!

模拟代码

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

4.2 解决方案

解决思路:定义类成员要谨慎

方案1

有公用的内容且不修改,可以定义在Servlet的成员变量位置。

// java代码保持不变

方案2:(极不推荐)

公用的内容有可能要修改,使用线程锁(同步机制)加锁核心代码

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

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 加锁,保证对共享数据的修改时安全的,但是性能太低,不推荐使用
        synchronized (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);
    }
}

方案3:(推荐方式)

局部变量:非公用的内容可以定义在doGet或者doPost方法中,这里的变量每次方法调用时都会重新创建,不是共享数据,不存在线程安全问题。

/*
    Servlet线程安全
 */
public class ServletDemo04 extends HttpServlet{
    //1.定义用户名成员变量。在单例的Servlet中,很少去定义成员变量,多是定义成局部变量在doget/doPost中
    //private String username = null;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = null;
        //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);
    }
}

5. Servlet配置相关

5.1 Servlet 映射方式

在web-app中通过Servlet-mapping标签,配置一个规则,用于匹配 请求资源路径处理该请求(资源路径)的Servlet

5.1.1 四中映射匹配方式

  • 第一种:唯一匹配(现阶段)
    写法:/虚拟路径具体名称的方式。

    ​ 访问的资源路径必须和映射配置完全相同。
    ​ javaweb学习阶段主要用法

  • 第二种:前缀匹配
    写法:/xxx开头 + 通配符的方式。
    只要请求的资源路径符合开头内容即可,不用考虑结尾是什么。

  • 第三种:扩展名匹配
    写法:通配符 + 固定格式结尾的方式。注意:不能以/ 开头
    只要符合固定结尾格式请求就会被拦截,并由对应的Servlet处理,不用考虑前面的路径。
    *.do: strust1写法 *.action struct2写法 *.html(伪静态,有利于搜索引擎优化)

  • 第四种:默认缺省匹配(框架阶段)
    写法:/ ,现阶段不用我们配置,tomcat默认配置。

    ​ 上述所有都不能匹配的时候,由该默认缺省匹配来处理,也就是让DefaultServlet来处理。
    ​ tomcat中有一个名称为DefaultServlet,就是和/绑定在一起的,最终就是有这个servlet来处理的
    ​ 找不到请求资源时报错、请求资源路径是一个静态资源等情况

    注意:

    1. 优先级问题:越是具体的优先级越高,越是模糊通用的优先级越低。第一种 -> 第二种 -> 第三种 > 第四种。
    2. 不能在第三种拓展名匹配的同时,使用/开头
    3. 映射相关在之后的SpringMVC框架中会使用

    5.1.2 演示代码

    ServletDemo05.java

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

    web.xml

    <!--演示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>-->
    <!-- 前缀匹配 /开头+通配符的方式-->
    <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>
    <!--后缀匹配:通配符+固定格式结尾的方式-->
    <servlet>
        <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>
    

    5.1.3 DefaultServlet

  • <!-- 113行 声明一个名叫default的Servlet -->
    <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <!-- 434 行  default的Servlet的映射配置 -->
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    

5.2 Servlet 多路径映射

可以通过通配符的形式,给同一个 Servlet 配置多个访问映射,从而根据不同的请求路径来实现不同的功能。

  • 模拟场景

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

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

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

实现步骤

  1. 获取请求的URL
  2. 判断是否已对应的后缀,打不同的折扣

实际用法

请求相同的URL,但是携带的是不同的参数,根据参数判断执行不同的逻辑。

request.getRequestURI():获取请求的URI值

5.2.x 相关代码

ServletDemo06.java

/*
    Servlet 多路径映射
 */
public class ServletDemo06 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        int money = 1000;

        //获取访问的资源路径
        String name = req.getRequestURI();
        System.out.println("截串前name = " + name);
        name = name.substring(name.lastIndexOf("/"));
        System.out.println("截串后name = " + name);

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

web.xml

<servlet>
    <servlet-name>xxx</servlet-name>
    <servlet-class>com.itheima.servlet.ServletDemo06</servlet-class>
</servlet>

<!--
    <servlet-mapping>
        <servlet-name>xxx</servlet-name>
        <url-pattern>/vip</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>xxx</servlet-name>
        <url-pattern>/vvip</url-pattern>
    </servlet-mapping>
-->
<!-- 前缀匹配,让xxx Servlet处理多个相关请求  -->
<servlet-mapping>
    <servlet-name>xxx</servlet-name>
    <url-pattern>/itheima/*</url-pattern>
</servlet-mapping>

5.3 Servlet对象创建的时机

  • 第一次访问时才创建(默认)

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

    弊端:如果servlet实例化过程比较复杂,第一个访问的用户的体验会比较差。运行时出问题,影响较大。

  • 项目启动时就创建

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

    弊端:对服务器内存占用较多

  • 实现方式:修改 Servlet 创建时机

    <servlet>标签中,添加<load-on-startup>标签。

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

Servlet

<servlet>
    <servlet-name>servletDemo03</servlet-name>
    <servlet-class>com.itheima.servlet.ServletDemo03</servlet-class>
    <!--配置Servlet启动时机 0或正整数代表服务器启动时创建,数字越小,优先级越高。负数或不写代表第一次访问时创建-->
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>servletDemo03</servlet-name>
    <url-pattern>/servletDemo03</url-pattern>
</servlet-mapping>

5.4 默认 Servlet(理解)

默认 Servlet 是由Tomcat服务器提供的一个 Servlet。

它配置在 Tomcat 的 conf 目录中的 web.xml 中。

ServletServlet-namenamedefault,匹配的url-pattern/

5.4.1 默认Servlet的作用

客户端发送请求时,Tomcat服务器首先会在我们项目中的 web.xml 中查找映射配置,查看是否有Servlet的url-pattern匹配请求的路径。

  • 如果能找到,找到后就调用与之绑定的Servlet的service方法

  • 当所有的Servlet的url-pattern都不能匹配该请求时,就由/来匹配,并由与/匹配的默认Servlet来处理该请求。

    DefaultServlet会根据不同的情况,响应给浏览器不同的错误状态码和描述、静态资源也是由其来处理的。

  • 默认Servlet配置图示

Servlet

6. ServletConfig(理解)

  • ServletConfigServlet唯一对应
  • 用于保存当前Servlet的一些配置信息,这些配置信息是通过Servlet初始化参数配置并传递的。
  • 其生命周期与Servlet一致。

6.1 Servlet初始化参数(后面会用)

Servlet初始化参数的配置+获取

可以为某个Servlet配置0至多个初始化参数;这些参数属于仅当前Servlet;会在所属Servlet实例化的时,被加载进对应的ServletConfig对象中。

作用:在Servlet对象初始化时,为Servlet传递参数;可以通过servlet对应的ServletConfig对象获取

结构及配置方式:初始化参数由key和value的键值对组成。在标签下,格式如下:

<servlet>
   <!-- xxxx -->
    <init-param>
        <!-- 
            param-name:代表初始化参数的 key
            param-value:代表初始化参数的 value。
        -->
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</servlet>

应用场景SpringMVC加载配置文件使用

注意:

ServletConfig/Servlet初始化参数只对当前Servlet有效,独属于当前Servlet

6.2 ServletConfig概述

Servlet的规范中,允许为每一个Servlet 都提供初始化的参数,这些参数会被保存在 ServletConfig对象中。ServletServletConfig一对一唯一匹配。

作用:在Servlet 的初始化时,把一些配置参数(Servlet的初始化参数)传递给 Servlet。

生命周期:和 Servlet 相同。

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

如果某个Servlet配置了 <load-onstartup>非负整数</load-on-startup> ,那么对应的ServletConfig对象也会在应用项目加载时创建。

6.3 ServletConfig功能

  • 获取Servlet始化参数

    根据Servlet初始化参数的key获取到对应的value

    String getInitParameter(String name);  //根据参数名称获取参数的值
    
  • 获取所有初始化参数的name

    获取配置在web.xml中当前Servlet标签下Servlet-Name的值

    Enumeration<String> getInitParameterNames();   //获取所有参数名称的枚举
    

    获取ServletContext对象/ServletName

    String getServletName();  获取Servlet的名称
    ServletContext getServletContext();   获取ServletContext对象
    

6.4 ServletConfig对象获取

  • 原始方式

    在Servlet的类中重写init(ServletConfig config)方法,该方法形参位置就是一个ServletConfig对象
    这里的ServletConfig对象就是我们需要的ServletConfig对象,但是只能在inti方法中可用
    可以在Servlet成员位置定义一个成员变量,并在init方法中对其进行赋值,这样就可以在整个Servlet中使用了

    public class ServletConfigDemo extends HttpServlet {
    
        // 获取ServletConfig对象的方式1:
        //声明ServletConfig配置对象
        private ServletConfig config;
        
        /*
            通过init方法来为ServletConfig配置对象赋值
            tomcat帮我们创建好的一个ServletConfig对象,其内就包含了设置好的初始化参数
         */
        @Override
        public void init(ServletConfig config) throws ServletException {
            // 把局部变量config赋值给成员变量config,让其在整个类中都可以使用
            this.config = config;
        }
        
        // 在Servlet中使用
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //根据key获取value
            String encodingValue = config.getInitParameter("encoding");
            System.out.println(encodingValue);
         }
    }
    
  • 推荐方式

    Servlet父类定义了获取ServletConfig对象的方法,可以直接调用获取

    this.getServletConfig()

    public class ServletConfigDemo extends HttpServlet {
    
        // 在Servlet中使用
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            
        // 获取ServletConfig对象的方式2:通过servlet对象获取ServletConfig对象
        //声明ServletConfig配置对象
        private ServletConfig config = this.getServletConfig();
            
            //根据key获取value
            String encodingValue = config.getInitParameter("encoding");
            System.out.println(encodingValue);
         }
    }
    
  • 极力推荐的方式

    不获取ServletConfig对象
    而是直接使用当前Servlet对象封装的相同方法,实现ServletConfig的功能

    public class ServletConfigDemo extends HttpServlet {
    	// 获取ServletConfig对象的方式3. 不获取ServletConfig对象,直接用(Servlet对象)
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //根据key获取value
            String encodingValue = getInitParameter("encoding");
            System.out.println(encodingValue);
        }
    }
    

6.5 演示代码

web.xml

<!--配置Servlet-->
<servlet>
    <servlet-name>servletConfigDemo</servlet-name>
    <servlet-class>com.itheima.servlet.ServletConfigDemo</servlet-class>
    <!--配置ServletConfig初始化参数-->
    <init-param>
        <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>
package com.itheima.servlet;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
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.Enumeration;

/*
    ServletConfig的使用

    ServletConfig对象的获取方式
        1. 使用init(ServletConfig sc)来初始化并赋值

        2. 通过servlet对象获取ServletConfig对象

        3. 不获取ServletConfig对象,直接用(Servlet对象)
 */
public class ServletConfigDemo extends HttpServlet {

    /*
    // 获取ServletConfig对象的方式1: 使用init(ServletConfig sc)来初始化并赋值
    //声明ServletConfig配置对象
    private ServletConfig config;

    *//*
        通过init方法来为ServletConfig配置对象赋值
        tomcat帮我们创建好的一个ServletConfig对象,其内就包含了设置好的初始化参数
     *//*
    @Override
    public void init(ServletConfig config) throws ServletException {
        // 把局部变量config赋值给成员变量config,让其在整个类中都可以使用
        this.config = config;
    }



    // 获取ServletConfig对象的方式2:通过servlet对象获取ServletConfig对象
    private ServletConfig config = getServletConfig()

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


    */

   //  获取ServletConfig对象的方式3: 不获取ServletConfig对象,直接用(Servlet对象)

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //根据key获取value
        String encodingValue = getInitParameter("encoding");
        System.out.println(encodingValue);

        //获取Servlet的名称
        String servletName = getServletName();
        System.out.println(servletName);

        //获取所有的key
        Enumeration<String> names = getInitParameterNames();
        //遍历得到的key
        while(names.hasMoreElements()) {
            //获取每一个key
            String name = names.nextElement();
            //通过key获取value
            String value = getInitParameter(name);
            System.out.println("name:" + name + ",value:" + value);
        }

        //获取ServletContext对象
        ServletContext context = 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);
    }
}

7. ServletContext

7.1 ServletContext概述

ServletContext 是应用上下文对象。每一个应用(模块/项目/工程)中有且只有一个 ServletContext 对象。该对象是项目应用级别的。

Servlet&ServletContext对比

一个项目中可以有N个Servlet,每个Servlet都可以有一个Servlet实例,每个Servlet实例都可以有一个ServletConfig对象。

一个项目中只有一个ServletContext,对应的就是整个web项目。

**作用:**通过该对象可以获得 全局初始化参数作为域对象(大map集合)实现Servlet之间的数据共享

**生命周期:**与整个项目共存亡。

  • 创建: 应用一加载,该对象就会被被创建出来。 一个应用有且只有一个ServletContext实例对象。
  • 运行:只要应用一直提供服务,该对象就一直存在。
  • 销毁:应用被卸载(或者服务器挂了),该对象消亡。

作为域对象的图示

Servlet

7.2 全局初始化参数

可以为整个项目配置0至多个全局初始化参数;全局初始化参数属于整个项目,不独属于某个Servlet;会在项目启动加载的时候,被加载进ServletContext对象中。

**作用:**在项目启动(初始化ServletContext)时,为ServletContext传递参数;可以通过ServletContext获取。

结构及配置方式:初始化参数由key和value的键值对组成,在<web-app>标签中,通过<context-param>标签来配置。

<context-param>
    <!-- 
 		param-name:代表初始化参数的 key
		param-value:代表初始化参数的 value。
	-->
    <param-name>encoding</param-name>
    <param-value>UTF-8</param-value>
</context-param>

注意:

  1. 全局初始化参数可以在整个项目中任何位置(eg:任意一个Servlet中)通过ServletContext对象获取并使用
  2. 属于整个项目,而不独属于某个Servlet

7.3 ServletContext域对象

域对象指的是在一定范围内可以用于存值取值的容器对象。相当于一个map对象。

作用:域对象可以在其作用范围内实现数据共享。不同作用范围的域对象,共享数据的能力也不一样。

在Servlet规范中,一共有4个域对象。ServletContext域就是其中最大的一个,也叫 application 域。期间整个项目中可以使用该域对象任意存取值.

7.4 ServletContext功能

  • 获取全局初始化参数
  • 作为域对象实现Servlet之间的数据共享。
  • 获取项目中资源的绝对路径
  • 获取项目的虚拟路径

7.5 相关方法

  • 获取ServletContext对象本身

    ServletConfig对象的 getServletContext() 方法
    Servlet对象的       getServletContext() 方法
    
  • 与域对象概念相关

    void setAttribute(String name,Object value);	// 向域对象中存储数据
    Object getAttribute(String name);		// 通过名称获取域对象中存储的数据
    Enumeration<String> getAttributeNames();	// 获取域对象中所有数据的名称
    void removeAttribute(String name);		// 通过名称移除域对象中的数据
    
  • 与全局初始化参数相关

    String getInitParameter(String name);         // 根据名称获取全局配置的值
    Enumeration<String> getInitParamterNames();   // 获取全局配置的所有名称
    
  • 其他方法

    String getServletContextName();		// 获取ServletContext的名称
    String getContextPath();			// 获取当前应用的访问虚拟目录,即发布项目时的虚拟路径
    String getRealPath(String path);	// 根据虚拟目录获取应用部署的磁盘绝对路径
    String getServerInfo();				// 获取服务器的名称和版本信息
    
  • 工作中用法

    全局初始化参数使用场景:Spring框架加载Spring配置文件,需要使用全局初始化参数 + 监听器

    ServletContext域对象:需要在多个Servlet之间共享数据的场景,均适用。

    eg:SpringMVC共享SpringMVC容器对象的时候,使用的ServletContext域对象

7.6 案例代码

web.xml

<!--声明Servlet1-->
<servlet>
    <servlet-name>servletConfigDemo</servlet-name>
    <servlet-class>com.itheima.servlet.ServletConfigDemo</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>servletConfigDemo</servlet-name>
    <url-pattern>/servletConfigDemo</url-pattern>
</servlet-mapping>

<!--声明Servlet2-->
<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>
public class ServletContextDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //获取ServletContext对象
        ServletContext context = getServletContext();

        //获取全局配置的globalEncoding
        String value = context.getInitParameter("globalEncoding");
        System.out.println(value);


        // 获取全局初始化参数的所有name
        Enumeration<String> globalInitParameterNames = context.getInitParameterNames();

        //获取应用的访问虚拟目录/路径
        String contextPath = context.getContextPath();
        System.out.println(contextPath);



        //根据虚拟目录获取应用部署的磁盘绝对路径
        //获取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文件的绝对路径
        // 类路径:java代码编译后之后存的位置,不是src下面;
        //       对于web项目来说,是war包目录下/WEB-INF/classes
        String a = context.getRealPath("/WEB-INF/classes/a.txt");
        System.out.println(a);


        //向域对象中存储数据
        context.setAttribute("username","zhangsan");

        //移除域对象中username的数据
        //context.removeAttribute("username");
    }

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

在另一个Servlet中获取与域对象中数据

public class ServletConfigDemo extends HttpServlet {
     @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
     
        //获取ServletContextDemo设置共享的数据
        Object username = context.getAttribute("username");
        System.out.println(username);
    }
}

7.7 类路径

web项目中,类路径指的不是存放java代码的src目录,而是java代码编译之后运行的目录,一般是在war包目录的/WEB-INF/classes

8. 两个初始化参数异同

  • 相同点

    都可以配置在web.xml中,在对应的对象创建时,传递参数给对应的对象,以便在程序中使用。

  • 不同点

    不同点 Servlet初始化参数 全局初始化参数
    配置位置 Servlet标签下面使用<int-param>定义 在根标签下使用<context-param>定义
    程序中存储位置 Servlet对象实例化时,被加载进Servlet对象的ServletConfig对象中 在项目启动时,被加载进ServletContext对象中
    作用范围 属于某个Servlet,在该Servlet对象中可以任意取用 属于整个项目而非某个Servlet,可以在任意一个Servlet中任意取用
    应用场景 加载SpringMVC的配置文件 加载Spring的配置文件

9. 注解开发 Servlet

9.1 概述

Servlet规范从 3.0 版本开始,既保留了 2.5 版本的配置方式(xml),同时又支持了全新的注解配置方式。

使用注解可以零xml配置,完成之前在web.xml配置文件中配置的所有内容和功能.

注解也是配置方式的一种。整体上来说,注解配置相对于XML配置更简洁,提高开发、读程效率.

9.2 相关注解

  • @WebServlet

    标注在Servlet上,相当于在web.xml中配置servlet映射

  • 注解源码如下:

Servlet

9.3 注解开发实现步骤

创建一个 web 项目。

定义一个类,继承 HttpServlet。

重写 doGet 和 doPost 方法。

在类上使用 @WebServlet 注解配置 Servlet。

部署并启动项目,并通过浏览器测试。

9.4 案例代码

/*
    注解配置Servlet
 */
@WebServlet("/servletDemo1") // 配置Servlet映射路径
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);
    }
}

10.学生管理系统

效果演示

Servlet

实现步骤

创建一个 web 项目。

创建一个用于保存学生信息的 html 文件。

创建一个类,继承 HttpServlet。

重写 doGet 和 doPost 方法。

在 web.xml 文件中修改默认主页和配置 Servlet。

在 doGet 方法中接收表单数据保存到文件中,并响应给浏览器结果。

部署并启动项目。

通过浏览器测试。

纯代码配置Servlet映射

除了xml配置和注解配置创建Servlet对象并完成映射之外,我们还可以通过纯代码的方式完成。

按照Servlet定义的规范实现即可,过程稍复杂。

该方式依赖一个在3.0 版本加入了新的接口:

实现步骤

定义一个类,继承 HttpServlet。

重写 doGet 和 doPost 方法。

定义一个容器实现类,实现 ServletContainerInitializer 接口。

在 src 目录下创建一个 META-INF 的包。

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

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

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

在容器实现类中的 onStartup 方法中完成注册 Servlet。

部署并启动项目。

通过浏览器测试。

0. 其他

0.1 URL构成

Servlet

0.2 /的含义

在前台HTML中

相当于http://ip:端口号 不包含项目名(虚拟路径),所以需要我们请求资源的时候,手动指定项目名

一般情况下,我们在HTML中都使用绝对路径(以/开头,避免相对路径带来困扰)

在后台

web.xml、Servlet代码中

代表的是项目的根目录,包含了项目名(虚拟路径),相当于http://ip:端口号/项目虚拟路径

本文地址:https://blog.csdn.net/zhengxigan/article/details/110939405