栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

Servlet

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Servlet

目录
  • 1.Servlet 概述
  • 2.Servlet 实现方式
    • 2.1 实现 Servlet 接口
    • 2.2 继承 GenericServlet 抽象类
    • 2.3 继承 HttpServlet 抽象类
  • 3.Servlet 生命周期
  • 4.Servlet 线程安全问题
  • 5.Servlet 不同映射方式
  • 6.Servlet 创建时机
  • 7.默认 Servlet

1.Servlet 概述

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

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

核心方法:service(),任何客户端的请求都会经过该方法。

2.Servlet 实现方式 2.1 实现 Servlet 接口

实现所有的抽象方法。该方式支持最大程度的自定义。

TestServlet.java

package com.qdu;

import jakarta.servlet.*;

import java.io.IOException;

public class TestServlet implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {

    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("service方法执行了...");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {

    }
}

web.xml




    
    
        ServletFlag
        com.qdu.TestServlet
    

    
    
        ServletFlag
        /Servlet01
    

在浏览器中输入 localhost:8080/Servlet01,回车,在 IDEA 控制台输出 service方法执行了...。

执行流程:

2.2 继承 GenericServlet 抽象类

必须重写 service 方法,其他方法可选择重写。该方式让我们开发 Servlet 变得简单。但是这种方式和 HTTP 协议无关。

TestServlet.java

package com.qdu;

import jakarta.servlet.GenericServlet;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;

import java.io.IOException;

public class TestServlet extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("service方法执行了...");
    }
}

web.xml




    
    
        ServletFlag
        com.qdu.TestServlet
    

    
    
        ServletFlag
        /Servlet02
    

在浏览器中输入 localhost:8080/Servlet02,回车,在 IDEA 控制台输出 service方法执行了...。

执行过程:

2.3 继承 HttpServlet 抽象类

需要重写 doGet 和 doPost 方法。该方式表示请求和响应都需要和 HTTP 协议相关。

TestServlet.java

package com.qdu;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

public class TestServlet 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 {
        doGet(req, resp);
    }
}

web.xml




    
    
        ServletFlag
        com.qdu.TestServlet
    

    
    
        ServletFlag
        /Servlet03
    

在浏览器中输入 localhost:8080/Servlet03,回车,在 IDEA 控制台输出 方法执行了...。

3.Servlet 生命周期

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

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

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

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

TestServlet.java

package com.qdu;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

public class TestServlet 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("对象销毁了...");
    }
}

web.xml




    
    
        ServletFlag
        com.qdu.TestServlet
    

    
    
        ServletFlag
        /Servlet03
    

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

4.Servlet 线程安全问题

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

package com.qdu;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

public class TestServlet extends HttpServlet {
    private String username;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取用户名
        username = req.getParameter("username");

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

        // 将用户名响应给浏览器
        PrintWriter pw = resp.getWriter();
        pw.println("welcome:" + username);
        pw.close();
    }

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



    
    
        ServletFlag
        com.qdu.TestServlet
    

    
    
        ServletFlag
        /Servlet03
    

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

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

package com.qdu;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

public class TestServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String username = null;

        // 获取用户名
        username = req.getParameter("username");

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

        // 将用户名响应给浏览器
        PrintWriter pw = resp.getWriter();
        pw.println("welcome:" + username);
        pw.close();
    }

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

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

public class TestServlet extends HttpServlet {
    private String username;

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

        synchronized (this) {
            // 获取用户名
            username = req.getParameter("username");

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

            // 将用户名响应给浏览器
            PrintWriter pw = resp.getWriter();
            pw.println("welcome:" + username);
            pw.close();
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
5.Servlet 不同映射方式

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

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

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

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

package com.qdu;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

public class TestServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("TestServlet执行了...");
    }

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



    
    
        ServletFlag
        com.qdu.TestServlet
    
    
        ServletFlag
        /Servlet03
    




    
    
        ServletFlag
        com.qdu.TestServlet
    
    
        ServletFlag
        /servlet/*
    




    
    
        ServletFlag
        com.qdu.TestServlet
    
    
        ServletFlag
        *.do
    

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

示例:

  • 如果访问的资源路径是 /vip,商品价格打 9 折。
  • 如果访问的资源路径是 /vvip,商品价格打 5 折。
  • 如果访问的资源路径是其他,商品价格不打折。
package com.qdu;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;

public class TestServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        int money = 1000;

        // 获取访问的资源路径
        String name = req.getRequestURI();
        name = name.substring(name.lastIndexOf("/"));

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


    
    
        ServletFlag
        com.qdu.TestServlet
    
    
        ServletFlag
        /qdu/*
    

6.Servlet 创建时机

(1) 第一次访问时创建

  • 优势:减少对服务器内存的浪费。提高了服务器启动的效率。
  • 弊端:如果有一些要在应用加载时就做的初始化操作,无法完成。

(2) 服务器加载时创建

  • 优势:提前创建好对象,提高了首次执行的效率。可以完成一些应用加载时要做的初始化操作。
  • 弊端:对服务器内存占用较多,影响了服务器启动的效率。

修改 Servlet 创建时机:在 标签中,添加 标签。

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


    servletDemo03
    com.itheima.servlet.ServletDemo03
    
    1


    servletDemo03
    /servletDemo03

7.默认 Servlet

默认 Servlet 是由服务器提供的一个 Servlet。它配置在 Tomcat 的 conf 目录中的 web.xml 中。

它的映射路径是 /,我们在发送请求时,首先会在我们项目中的 web.xml 中查找映射配置,找到则执行。但是当找不到对应的 Servlet 路径时,就去找默认的 Servlet,由默认 Servlet 处理。所以,一切都是 Servlet。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/642186.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号