七、HTTPTomcatServlet

news2024/9/24 17:09:12

1,Web概述

1.1 Web和JavaWeb的概念

Web是全球广域网,也称为万维网(www),能够通过浏览器访问的网站。
在我们日常的生活中,经常会使用浏览器去访问百度京东传智官网等这些网站,这些网站统称为Web网站。如下就是通过浏览器访问传智官网的界面:

我们知道了什么是Web,那么JavaWeb又是什么呢?顾名思义JavaWeb就是用Java技术来解决相关web互联网领域的技术栈。
等学习完JavaWeb之后,同学们就可以使用Java语言开发我们上述所说的网站。而国内很多大型网站公司也是首选Java语言来解决web互联网相关的问题。那都有哪些公司的系统是使用Java语言的呢?

使用Java语言开发互联网系统是有很多技术栈需要大家了解,具体都有哪些呢?

1.2 JavaWeb技术栈

了解JavaWeb技术栈之前,有一个很重要的概念要介绍。

1.2.1 B/S架构

什么是B/S架构?
B/S 架构:Browser/Server,浏览器/服务器 架构模式,它的特点是,客户端只需要浏览器,应用程序的逻辑和数据都存储在服务器端。浏览器只需要请求服务器,获取Web资源,服务器把Web资源发送给浏览器即可。大家可以通过下面这张图来回想下我们平常的上网过程:

  • 打开浏览器访问百度首页,输入要搜索的内容,点击回车或百度一下,就可以获取和搜索相关的内容
  • 思考下搜索的内容并不在我们自己的点上,那么这些内容从何而来?答案很明显是从百度服务器返回给我们的
  • 日常百度的小细节,逢年过节百度的logo会更换不同的图片,服务端发生变化,客户端不需做任务事情就能获取最新内容
  • 所以说B/S架构的好处:易于维护升级:服务器端升级后,客户端无需任何部署就可以使用到新的版本。
    了解了什么是B/S架构后,作为后台开发工程师的我们将来主要关注的是服务端的开发和维护工作。在服务端将来会放很多资源,都有哪些资源呢?

CS架构:Client Server 客户端/服务器架构

1.2.2 静态资源

  • 静态资源主要包含HTML、CSS、JavaScript、图片等,主要负责页面的展示。
  • 我们之前已经学过前端网页制作三剑客(HTML+CSS+JavaScript),使用这些技术我们就可以制作出效果比较丰富的网页,将来展现给用户。但是由于做出来的这些内容都是静态的,这就会导致所有的人看到的内容将是一模一样。
  • 在日常上网的过程中,我们除了看到这些好看的页面以外,还会碰到很多动态内容,比如我们常见的百度登录效果:

张三登录以后在网页的右上角看到的是 张三,而李四登录以后看到的则是李四。所以不同的用户访问相同的资源看到的内容大多数是不一样的,要想实现这样的效果,光靠静态资源是无法实现的。

1.2.3 动态资源

  • 动态资源主要包含Servlet、JSP等,主要用来负责逻辑处理。
  • 动态资源处理完逻辑后会把得到的结果交给静态资源来进行展示,动态资源和静态资源要结合一起使用。
  • 动态资源虽然可以处理逻辑,但是当用户来登录百度的时候,就需要输入用户名密码,这个时候我们就又需要解决的一个问题是,用户在注册的时候填入的用户名和密码、以及我们经常会访问到一些数据列表的内容展示(如下图所示),这些数据都存储在哪里?我们需要的时候又是从哪里来取呢?

动态资源和静态资源最大的区别就是数据是动态获取的,通过逻辑计算出来或者通过代码从持久化存储的数据中拿到的。不同的用户访问时,静态资源始终不变,而动态资源可能会发生变化。

动态资源最终都需要转成静态资源,让浏览器渲染展示的。

1.2.4 数据库

  • 数据库主要负责存储数据。
  • 整个Web的访问过程就如下图所示:

(1)浏览器发送一个请求到服务端,去请求所需要的相关资源;
(2)资源分为动态资源和静态资源,动态资源可以是使用Java代码按照Servlet和JSP的规范编写的内容;
(3)在Java代码可以进行业务处理也可以从数据库中读取数据;
(4)拿到数据后,把数据交给HTML页面进行展示,再结合CSS和JavaScript使展示效果更好;
(5)服务端将静态资源响应给浏览器;
(6)浏览器将这些资源进行解析;
(7)解析后将效果展示在浏览器,用户就可以看到最终的结果。
在整个Web的访问过程中,会设计到很多技术,这些技术有已经学习过的,也有还未涉及到的内容,都有哪些还没有涉及到呢?

1.2.5 HTTP协议

  • HTTP协议:主要定义通信规则
  • 浏览器发送请求给服务器,服务器响应数据给浏览器,这整个过程都需要遵守一定的规则,之前大家学习过TCP、UDP,这些都属于规则,这里我们需要使用的是HTTP协议,这也是一种规则。

请求响应模型:在http协议中,客户端和服务器通过请求和响应交互的。一次请求对应一次响应。

请求:客户端发给服务器端。

响应:服务器发给客户端。

1.2.6 Web服务器

  • Web服务器:负责解析 HTTP 协议,解析请求数据,并发送响应数据
  • 浏览器按照HTTP协议发送请求和数据,后台就需要一个Web服务器软件来根据HTTP协议解析请求和数据,然后把处理结果再按照HTTP协议发送给浏览器
  • Web服务器软件有很多,我们课程中将学习的是目前最为常用的Tomcat服务器

到这为止,关于JavaWeb中用到的技术栈我们就介绍完了,这里面就只有HTTP协议、Servlet、JSP以及Tomcat这些知识是没有学习过的,所以整个Web核心主要就是来学习这些技术。

1.3 Web核心课程安排

整个Web核心,我们总共有六天的学习内容,分别是:

  • 第一天:HTTP、Tomcat、Servlet
  • 第二天:Request(请求)、Response(响应)
  • 第三天:JSP、会话技术(Cookie、Session)
  • 第四天:Filter(过滤器)、Listener(监听器)
  • 第五天:Ajax、Vue、ElementUI
  • 第六天:综合案例

(1)Request是从客户端向服务端发出的请求对象,

(2)Response是从服务端响应给客户端的结果对象,

(3)JSP是动态网页技术,

(4)会话技术是用来存储客户端和服务端交互所产生的数据,

(5)过滤器是用来拦截客户端的请求,

(6)监听器是用来监听特定事件,

(7)Ajax、Vue、ElementUI都是属于前端技术

这些技术都该如何来使用,我们后面会一个个进行详细的讲解。接下来我们来学习下HTTP、Tomcat和Servlet。

2, HTTP

2.1 简介

HTTP概念

HyperText Transfer Protocol,超文本传输协议,规定了浏览器和服务器之间数据传输的规则

  • 数据传输的规则指的是请求数据和响应数据需要按照指定的格式进行传输。
  • 如果想知道具体的格式,可以打开浏览器,点击F12打开开发者工具,点击Network来查看某一次请求的请求数据和响应数据具体的格式内容,如下图所示:

注意:在浏览器中如果看不到上述内容,需要清除浏览器的浏览数据。chrome浏览器可以使用ctrl+shift+Del进行清除。

所以学习HTTP主要就是学习请求和响应数据的具体格式内容。

HTTP协议特点

HTTP协议有它自己的一些特点,分别是:

  • 基于TCP协议: 面向连接,安全

    TCP是一种面向连接的(建立连接之前是需要经过三次握手)、可靠的、基于字节流的传输层通信协议,在数据传输方面更安全。

  • 基于请求-响应模型的:一次请求对应一次响应

    请求和响应是一一对应关系

  • HTTP协议是无状态协议:对于事物处理没有记忆能力。每次请求-响应都是独立的

    无状态指的是客户端发送HTTP请求给服务端之后,服务端根据请求响应数据,响应完后,不会记录任何信息。这种特性有优点也有缺点,

    • 缺点:多次请求间不能共享数据
    • 优点:速度快

    请求之间无法共享数据会引发的问题,如:

    • 京东购物,加入购物车去购物车结算是两次请求,
    • HTTP协议的无状态特性,加入购物车请求响应结束后,并未记录加入购物车是何商品
    • 发起去购物车结算的请求后,因为无法获取哪些商品加入了购物车,会导致此次请求无法正确展示数据

    具体使用的时候,我们发现京东是可以正常展示数据的,原因是Java早已考虑到这个问题,并提出了使用会话技术(Cookie、Session)来解决这个问题。具体如何来做,我们后面会详细讲到。刚才提到HTTP协议是规定了请求和响应数据的格式,那具体的格式是什么呢?

2.2 请求数据格式

2.2.1 格式介绍

请求数据总共分为三部分内容,分别是请求行请求头请求体

  • 请求行: HTTP请求中的第一行数据,请求行包含三块内容,分别是 GET[请求方式] /[请求URL路径] HTTP/1.1[HTTP协议及版本]

    请求方式有七种,最常用的是GET和POST

  • 请求头: 第二行开始,格式为key: value形式。value有可能会有多个。

    请求头中会包含若干个属性,常见的HTTP请求头有:

    Host: 表示请求的主机名
    User-Agent: 浏览器版本,例如Chrome浏览器的标识类似Mozilla/5.0 ...Chrome/79,IE浏览器的标识类似Mozilla/5.0 (Windows NT ...)like Gecko;
    Accept:表示浏览器能接收的资源类型,如text/*,image/*或者*/*表示所有;
    Accept-Language:表示浏览器偏好的语言,服务器可以据此返回不同语言的网页;
    Accept-Encoding:表示浏览器可以支持的压缩类型,例如gzip, deflate等。
    

    这些数据有什么用处?

    举例说明:服务端可以根据请求头中的内容来获取客户端的相关信息,有了这些信息服务端就可以处理不同的业务需求,比如:

    • 不同浏览器解析HTML和CSS标签的结果会有不一致,所以就会导致相同的代码在不同的浏览器会出现不同的效果
    • 服务端根据客户端请求头中的数据获取到客户端的浏览器类型,就可以根据不同的浏览器设置不同的代码来达到一致的效果
    • 这就是我们常说的浏览器兼容问题
  • 请求体: POST请求的最后一部分,存储请求参数

    如上图红线框的内容就是请求体的内容,请求体和请求头之间是有一个空行隔开。此时浏览器发送的是POST请求,为什么不能使用GET呢?这时就需要回顾GET和POST两个请求之间的区别了:

    • GET请求请求参数在请求行中,没有请求体,POST请求请求参数在请求体中
    • GET请求请求参数大小有限制,POST没有
  • 实际上,POST请求也可以在url中拼接参数进行传递。

2.2.2 实例演示

代码\http 拷贝到IDEA的工作目录中,比如D:\workspace\web目录,

使用IDEA打开

打开后,可以点击项目中的html\19-表单验证.html,使用浏览器打开,通过修改页面中form表单的method属性来测试GET请求和POST请求的参数携带方式。

小结:

  1. 请求数据中包含三部分内容,分别是请求行、请求头和请求体

  2. POST请求数据在请求体中,GET请求数据在请求行上

2.3 响应数据格式

2.3.1 格式介绍

响应数据总共分为三部分内容,分别是响应行响应头响应体

  • 响应行:响应数据的第一行,响应行包含三块内容,分别是 HTTP/1.1[HTTP协议及版本] 200[响应状态码] ok[状态码的描述]

  • 响应头:第二行开始,格式为key:value形式

    响应头中会包含若干个属性,常见的HTTP响应头有:

    Content-Type:表示该响应内容的类型,例如text/html,image/jpeg;
    Content-Length:表示该响应内容的长度(字节数);
    Content-Encoding:表示该响应压缩算法,例如gzip;
    Cache-Control:指示客户端应如何缓存,例如max-age=300表示可以最多缓存300秒
    
  • 响应体: 最后一部分。存放响应数据

    上图中…这部分内容就是响应体,它和响应头之间有一个空行隔开。

2.3.2 响应状态码

参考: 资料/1.HTTP/《响应状态码.md》

关于响应状态码,我们先主要认识三个状态码,其余的等后期用到了再去掌握:

  • 200 ok 客户端请求成功
  • 403 Forbiden 权限不足
  • 404 Not Found 请求资源不存在
  • 405 Method Not Allowed 请求方式不支持
  • 500 Internal Server Error 服务端发生不可预期的错误

2.3.3 自定义服务器

在前面我们导入到IDEA中的http项目中,有一个Server.java类,这里面就是自定义的一个服务器代码,主要使用到的是ServerSocketSocket

package com.itheima;

import sun.misc.IOUtils;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
/*
    自定义服务器
 */
public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(8080); // 监听指定端口
        System.out.println("server is running...");
        while (true){
            Socket sock = ss.accept();
            System.out.println("connected from " + sock.getRemoteSocketAddress());
            Thread t = new Handler(sock);
            t.start();
        }
    }
}

class Handler extends Thread {
    Socket sock;

    public Handler(Socket sock) {
        this.sock = sock;
    }

    public void run() {
        try (InputStream input = this.sock.getInputStream()) {
            try (OutputStream output = this.sock.getOutputStream()) {
                handle(input, output);
            }
        } catch (Exception e) {
            try {
                this.sock.close();
            } catch (IOException ioe) {
            }
            System.out.println("client disconnected.");
        }
    }

    private void handle(InputStream input, OutputStream output) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8));
        // 读取HTTP请求:
        boolean requestOk = false;
        String first = reader.readLine();
        if (first.startsWith("GET / HTTP/1.")) {
            requestOk = true;
        }
        for (;;) {
            String header = reader.readLine();
            if (header.isEmpty()) { // 读取到空行时, HTTP Header读取完毕
                break;
            }
            System.out.println(header);
        }
        System.out.println(requestOk ? "Response OK" : "Response Error");
        if (!requestOk) {
            // 发送错误响应:
            writer.write("HTTP/1.0 404 Not Found\r\n");
            writer.write("Content-Length: 0\r\n");
            writer.write("\r\n");
            writer.flush();
        } else {
            // 发送成功响应:

            //读取html文件,转换为字符串
            BufferedReader br = new BufferedReader(new FileReader("http/html/a.html"));
            StringBuilder data = new StringBuilder();
            String line = null;
            while ((line = br.readLine()) != null){
                data.append(line);
            }
            br.close();
            int length = data.toString().getBytes(StandardCharsets.UTF_8).length;

            writer.write("HTTP/1.1 200 OK\r\n");
            writer.write("Connection: keep-alive\r\n");
            writer.write("Content-Type: text/html\r\n");
            writer.write("Content-Length: " + length + "\r\n");
            writer.write("\r\n"); // 空行标识Header和Body的分隔
            writer.write(data.toString());
            writer.flush();
        }
    }
}

上面代码,大家不需要自己写,主要通过上述代码,只需要大家了解到服务器可以使用java完成编写,是可以接受页面发送的请求和响应数据给前端浏览器的,真正用到的Web服务器,我们不会自己写,都是使用目前比较流行的web服务器,比如Tomcat

小结

  1. 响应数据中包含三部分内容,分别是响应行、响应头和响应体

  2. 掌握200,404,500这三个响应状态码所代表含义,分布是成功、所访问资源不存在和服务的错误

3, Tomcat

3.1 简介

3.1.1 什么是Web服务器

Web服务器是一个程序(软件),对HTTP协议的操作进行封装,使得程序员不必直接对协议进行操作,让Web开发更加便捷。主要功能是"提供网上信息浏览服务"。

在这里插入图片描述

Web服务器是安装在服务器端的一款软件,将来我们把自己写的Web项目部署到Web Tomcat服务器软件中,当Web服务器软件启动后,部署在Web服务器软件中的页面就可以直接通过浏览器来访问了。

Web服务器软件使用步骤

  • 准备静态资源
  • 下载安装Web服务器软件
  • 将静态资源部署到Web服务器上
  • 启动Web服务器使用浏览器访问对应的资源

上述内容在演示的时候,使用的是Apache下的Tomcat软件,至于Tomcat软件如何使用,后面会详细的讲到。而对于Web服务器来说,实现的方案有很多,Tomcat只是其中的一种,而除了Tomcat以外,还有很多优秀的Web服务器,比如:

Tomcat就是一款软件,我们主要是以学习如何去使用为主。具体我们会从以下这些方向去学习:

  1. 简介: 初步认识下Tomcat

  2. 基本使用: 安装、卸载、启动、关闭、配置和项目部署,这些都是对Tomcat的基本操作

  3. IDEA中如何创建Maven Web项目

  4. IDEA中如何使用Tomcat,后面这两个都是我们以后开发经常会用到的方式

首选我们来认识下Tomcat。

Tomcat

Tomcat的相关概念:

  • Tomcat是Apache软件基金会一个核心项目,是一个开源免费的轻量级Web服务器,支持Servlet/JSP少量JavaEE规范。

  • 概念中提到了JavaEE规范,那什么又是JavaEE规范呢?

    JavaEE: Java Enterprise Edition,Java企业版。指Java企业级开发的技术规范总和。包含13项技术规范:JDBC、JNDI、EJB、RMI、JSP、Servlet、XML、JMS、Java IDL、JTS、JTA、JavaMail、JAF。

  • 因为Tomcat支持Servlet/JSP规范,所以Tomcat也被称为Web容器、Servlet容器。Servlet需要依赖Tomcat才能运行。

  • Tomcat的官网: https://tomcat.apache.org/ 从官网上可以下载对应的版本进行使用。

Tomcat的LOGO

小结

通过这一节的学习,我们需要掌握以下内容:

  1. Web服务器的作用

封装HTTP协议操作,简化开发

可以将Web项目部署到服务器中,对外提供网上浏览服务

  1. Tomcat是一个轻量级的Web服务器,支持Servlet/JSP少量JavaEE规范,也称为Web容器,Servlet容器。

3.2 基本使用

Tomcat总共分两部分学习,先来学习Tomcat的基本使用,包括Tomcat的下载、安装、卸载、启动和关闭

3.2.1 下载

直接从官网下载

大家可以自行下载,也可以直接使用资料中已经下载好的资源,

Tomcat的软件程序 资料/2. Tomcat/apache-tomcat-8.5.68-windows-x64.zip

Tomcat的源码 资料/2. Tomcat/tomcat源码/apache-tomcat-8.5.68-src.zip

3.2.2 安装

Tomcat是绿色版,直接解压即可

  • 在D盘的software目录下,将apache-tomcat-8.5.68-windows-x64.zip进行解压缩,会得到一个apache-tomcat-8.5.68的目录,Tomcat就已经安装成功。

    注意,Tomcat在解压缩的时候,解压所在的目录可以任意,但最好解压到一个不包含中文和空格的目录,因为后期在部署项目的时候,如果路径有中文或者空格可能会导致程序部署失败。

  • 打开apache-tomcat-8.5.68目录就能看到如下目录结构,每个目录中包含的内容需要认识下,

    bin:目录下有两类文件,一种是以.bat结尾的,是Windows系统的可执行文件,一种是以.sh结尾的,是Linux系统的可执行文件。

    webapps:就是以后项目部署的目录

    到此,Tomcat的安装就已经完成。

启动时闪退

​ 可以把startup.bat拖入cmd窗口,查看错误信息。

  1. 缺少JDK。
  	JAVA_HOME   :JDK根目录
  	JRE_HOME	:JDK目录下的jre目录。
  2. 缺少tomcat自身的home目录作为环境变量(在idea中不需要设置,自动配置)。CATALINA_HOME。该变量的值设置为tomcat的根目录即可。
  3. 端口冲突。tomcat默认端口号为8080。如果该端口被占用,则无法启动。netstat -ano | findstr 8080。找到占用了该端口的应用,强制关闭。还可以更换tomcat端口。

3.2.3 卸载

卸载比较简单,可以直接删除目录即可

3.2.4 启动

双击: bin\startup.bat

启动后,通过浏览器访问 http://localhost:8080能看到Apache Tomcat的内容就说明Tomcat已经启动成功。

注意: 启动的过程中,控制台有中文乱码,需要修改conf/logging.properties

3.2.5 关闭

关闭有三种方式

  • 直接x掉运行窗口:强制关闭[不建议]
  • bin\shutdown.bat:正常关闭
  • ctrl+c: 正常关闭

3.2.6 配置

修改端口

  • Tomcat默认的端口是8080,要想修改Tomcat启动的端口号,需要修改 conf/server.xml

注: HTTP协议默认端口号为80,如果将Tomcat端口号改为80,则将来访问Tomcat时,将不用输入端口号。

启动时可能出现的错误

  • Tomcat的端口号取值范围是0-65535之间任意未被占用的端口,如果设置的端口号被占用,启动的时候就会包如下的错误

  • Tomcat启动的时候,启动窗口一闪而过: 需要检查JAVA_HOME环境变量是否正确配置

3.2.7 部署

  • Tomcat部署项目: 将项目放置到webapps目录下,即部署完成。

    • 资料/2. Tomcat/hello 目录拷贝到Tomcat的webapps目录下

    • 通过浏览器访问http://localhost/hello/a.html,能看到下面的内容就说明项目已经部署成功。

      但是呢随着项目的增大,项目中的资源也会越来越多,项目在拷贝的过程中也会越来越费时间,该如何解决呢?

  • 一般JavaWeb项目会被打包称war包,然后将war包放到Webapps目录下,Tomcat会自动解压缩war文件

    • 资料/2. Tomcat/haha.war目录拷贝到Tomcat的webapps目录下

    • Tomcat检测到war包后会自动完成解压缩,在webapps目录下就会多一个haha目录

    • 通过浏览器访问http://localhost/haha/a.html,能看到下面的内容就说明项目已经部署成功。

在这里插入图片描述

至此,Tomcat的部署就已经完成了,至于如何获得项目对应的war包,后期我们会借助于IDEA工具来生成。

jar:java归档文件

war:web归档文件

3.3 Maven创建Web项目

介绍完Tomcat的基本使用后,我们来学习在IDEA中如何创建Maven Web项目,学习这种方式的原因是以后Tomcat中运行的绝大多数都是Web项目,而使用Maven工具能更加简单快捷的把Web项目给创建出来,所以Maven的Web项目具体如何来构建呢?

在真正创建Maven Web项目之前,我们先要知道Web项目长什么样子,具体的结构是什么?

3.3.1 Web项目结构

Web项目的结构分为:开发中的项目和开发完可以部署的Web项目,这两种项目的结构是不一样的,我们一个个来介绍下:

  • Maven Web项目结构: 开发中的项目

  • 开发完成部署的Web项目

    • 开发项目通过执行Maven打包命令package,可以获取到部署的Web项目目录
    • 编译后的Java字节码文件和resources的资源文件,会被放到WEB-INF下的classes目录下
    • pom.xml中依赖坐标对应的jar包,会被放入WEB-INF下的lib目录下

3.3.2 创建Maven Web项目

介绍完Maven Web的项目结构后,接下来使用Maven来创建Web项目,创建方式有两种:使用骨架和不使用骨架

使用骨架

具体的步骤包含:

1.创建Maven项目

2.选择使用Web项目骨架

3.输入Maven项目坐标创建项目

4.确认Maven相关的配置信息后,完成项目创建

5.删除pom.xml中多余内容

6.补齐Maven Web项目缺失的目录结构

  1. 创建Maven项目

  2. 选择使用Web项目骨架

  3. 输入Maven项目坐标创建项目

  4. 确认Maven相关的配置信息后,完成项目创建

  5. 删除pom.xml中多余内容,只留下面的这些内容,注意打包方式 jar和war的区别

  6. 补齐Maven Web项目缺失的目录结构,默认没有java和resources目录,需要手动完成创建补齐,最终的目录结果如下

不使用骨架

具体的步骤包含:

1.创建Maven项目

2.选择不使用Web项目骨架

3.输入Maven项目坐标创建项目

4.在pom.xml设置打包方式为war

5.补齐Maven Web项目缺失webapp的目录结构

6.补齐Maven Web项目缺失WEB-INF/web.xml的目录结构

  1. 创建Maven项目

  2. 选择不使用Web项目骨架

  3. 输入Maven项目坐标创建项目

  4. 在pom.xml设置打包方式为war,默认是不写代表打包方式为jar

  5. 补齐Maven Web项目缺失webapp的目录结构

  6. 补齐Maven Web项目缺失WEB-INF/web.xml的目录结构

  7. 补充完后,最终的项目结构如下:

    在这里插入图片描述

上述两种方式,创建的web项目,都不是很全,需要手动补充内容,至于最终采用哪种方式来创建Maven Web项目,都是可以的,根据各自的喜好来选择使用即可。

热部署

​ 当源码发生改变时,项目会自动部署最新文件到服务器中。

小结

1.掌握Maven Web项目的目录结构

2.掌握使用骨架的方式创建Maven Web项目

3.掌握不使用骨架的方式创建Maven Web项目

3.4 IDEA使用Tomcat

  • Maven Web项目创建成功后,通过Maven的package命令可以将项目打包成war包,将war文件拷贝到Tomcat的webapps目录下,启动Tomcat就可以将项目部署成功,然后通过浏览器进行访问即可。
  • 然而我们在开发的过程中,项目中的内容会经常发生变化,如果按照上面这种方式来部署测试,是非常不方便的
  • 如何在IDEA中能快速使用Tomcat呢?

在IDEA中集成使用Tomcat有两种方式,分别是集成本地TomcatTomcat Maven插件

3.4.1 集成本地Tomcat

目标: 将刚才本地安装好的Tomcat8集成到IDEA中,完成项目部署,具体的实现步骤

  1. 打开添加本地Tomcat的面板

  2. 指定本地Tomcat的具体路径

  1. 修改Tomcat的名称,此步骤可以不改,只是让名字看起来更有意义,HTTP port中的端口也可以进行修改,比如把8080改成80

  2. 将开发项目部署项目到Tomcat中

    扩展内容: xxx.war和 xxx.war exploded这两种部署项目模式的区别?

    • war模式是将WEB工程打成war包,把war包发布到Tomcat服务器上

    • war exploded模式是将WEB工程以当前文件夹的位置关系发布到Tomcat服务器上

    • war模式部署成功后,Tomcat的webapps目录下会有部署的项目内容

    • war exploded模式部署成功后,Tomcat的webapps目录下没有,而使用的是项目的target目录下的内容进行部署

    • 建议大家都选war模式进行部署,更符合项目部署的实际情况

  3. 部署成功后,就可以启动项目,为了能更好的看到启动的效果,可以在webapp目录下添加a.html页面

  4. 启动成功后,可以通过浏览器进行访问测试

  5. 最终的注意事项

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I9WjH0JL-1677376156539)(assets/1627232916624.png)]

至此,IDEA中集成本地Tomcat进行项目部署的内容我们就介绍完了,整体步骤如下,大家需要按照流程进行部署操作练习。

在这里插入图片描述

热部署:

​ 在服务器启动过程中对资源进行修改,服务器会自动更新资源到服务器上。建议只选择Update Resources,代表资源更改时自动热部署。此时代码修改还是需要重启。

在这里插入图片描述

配置idea的快捷访问模板快速插入tomcat插件

  1. 建立一个组,随便起名。便于后期查找

在这里插入图片描述

  1. 在组中创建一个模板,指定快捷命令及替换的内容。在下方指定文件类型。后期就可以在指定的文件类型中输入自定义的命令,自动替换为内容。

3.4.2 Tomcat Maven插件

在IDEA中使用本地Tomcat进行项目部署,相对来说步骤比较繁琐,所以我们需要一种更简便的方式来替换它,那就是直接使用Maven中的Tomcat插件来部署项目,具体的实现步骤,只需要两步,分别是:

  1. 在pom.xml中添加Tomcat插件

       <build>
            <plugins>
                <!--Tomcat插件 -->
                <plugin>
                    <groupId>org.apache.tomcat.maven</groupId>
                    <artifactId>tomcat7-maven-plugin</artifactId>
                    <version>2.2</version>
                    <configuration>
                        <!--配置虚拟目录-->
                        <path>/</path>
                        <!--tomcat的端口号。默认是8080-->
                        <port>80</port>
                        <!--防止get请求时url中包含中文的乱码问题-->
                        <uriEncoding>UTF-8</uriEncoding>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
  2. 使用Maven Helper插件快速启动项目,选中项目,右键–>Run Maven --> tomcat7:run

注意:

  • 如果选中项目并右键点击后,看不到Run Maven和Debug Maven,这个时候就需要在IDEA中下载Maven Helper插件,具体的操作方式为: File --> Settings --> Plugins --> Maven Helper —> Install,安装完后按照提示重启IDEA,就可以看到了。

  • Maven Tomcat插件目前只有Tomcat7版本,没有更高的版本可以使用
  • 使用Maven Tomcat插件,要想修改Tomcat的端口和访问路径,可以直接修改pom.xml
<build>
    <plugins>
    	<!--Tomcat插件 -->
        <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <version>2.2</version>
            <configuration>
            	<port>80</port><!--访问端口号 -->
                <!--项目访问路径
					未配置访问路径: http://localhost:80/tomcat-demo2/a.html
					配置/后访问路径: http://localhost:80/a.html
					如果配置成 /hello,访问路径会变成什么?
						答案: http://localhost:80/hello/a.html
				-->
                <path>/</path>
                <!--解决get方式用url传参的时候的中文乱码问题,此问题仅在tomcat7及以下版本出现-->
                <uriEncoding>UTF-8</uriEncoding>
            </configuration>
        </plugin>
    </plugins>
</build>

虚拟目录:代表当前项目部署完毕后,在url中访问的项目路径,此路径不一定真实存在。

小结

通过这一节的学习,大家要掌握在IDEA中使用Tomcat的两种方式,集成本地Tomcat和使用Maven的Tomcat插件。后者更简单,推荐大家使用,但是如果对于Tomcat的版本有比较高的要求,要在Tomcat7以上,这个时候就只能用前者了。

本地tomcat部署项目时,如果无法选择war包,需要手动创建war包。选择部署时,一定要选择exploed(展开解压缩的方式部署,对热部署支持比较好)

WEB-INF目录对客户端不可见,不要在这里面创建页面。(到讲完转发跳转之后才可以在这里面放可访问资源)

4, Servlet

4.1 简介

  • Servlet是JavaWeb最为核心的内容,它是Java提供的一门动态web资源开发技术。

  • 使用Servlet就可以实现,根据不同的登录用户在页面上动态显示不同内容。

  • Servlet是JavaEE规范之一,其实就是一个接口,将来我们需要定义Servlet类实现Servlet接口,并由web服务器运行Servlet

介绍完Servlet是什么以后,接下来我们就按照快速入门->执行流程->生命周期->体系结构->urlPattern配置->XML配置的学习步骤,一步步完成对Servlet的知识学习,首选我们来通过一个入门案例来快速把Servlet用起来。

4.2 快速入门

需求分析: 编写一个Servlet类,并使用IDEA中Tomcat插件进行部署,最终通过浏览器访问所编写的Servlet程序。

具体的实现步骤为:

  1. 创建Web项目web-demo,导入Servlet依赖坐标
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <!--
      此处为什么需要添加该标签?
      provided指的是在编译和测试过程中有效,最后生成的war包时不会加入
       因为Tomcat的lib目录中已经有servlet-api这个jar包,如果在生成war包的时候生效就会和Tomcat中的jar包冲突,导致报错
    -->
    <scope>provided</scope>
</dependency>
  1. 创建:定义一个类,实现Servlet接口,并重写接口中所有方法,并在service方法中输入一句话
package com.itheima.web;

import javax.servlet.*;
import java.io.IOException;

public class ServletDemo1 implements Servlet {

    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("servlet hello world~");
    }
    public void init(ServletConfig servletConfig) throws ServletException {

    }

    public ServletConfig getServletConfig() {
        return null;
    }

    public String getServletInfo() {
        return null;
    }

    public void destroy() {

    }
}
  1. 配置:在类上使用@WebServlet注解,配置该Servlet的访问路径
@WebServlet("/demo1")
  1. 访问:启动Tomcat,浏览器中输入URL地址访问该Servlet
http://localhost:8080/web-demo/demo1
  1. 器访问后,在控制台会打印servlet hello world~ 说明servlet程序已经成功运行。

至此,Servlet的入门案例就已经完成,大家可以按照上面的步骤进行练习了。

4.3 执行流程

Servlet程序已经能正常运行,但是我们需要思考个问题: 我们并没有创建ServletDemo1类的对象,也没有调用对象中的service方法,为什么在控制台就打印了servlet hello world~这句话呢?

要想回答上述问题,我们就需要对Servlet的执行流程进行一个学习。

  • 浏览器发出http://localhost:8080/web-demo/demo1请求,从请求中可以解析出三部分内容,分别是localhost:8080web-demodemo1
    • 根据localhost:8080可以找到要访问的Tomcat Web服务器
    • 根据web-demo可以找到部署在Tomcat服务器上的web-demo项目
    • 根据demo1可以找到要访问的是项目中的哪个Servlet类,根据@WebServlet后面的值进行匹配
  • 找到ServletDemo1这个类后,Tomcat Web服务器就会为ServletDemo1这个类创建一个对象,然后调用对象中的service方法
    • ServletDemo1实现了Servlet接口,所以类中必然会重写service方法供Tomcat Web服务器进行调用
    • service方法中有ServletRequest和ServletResponse两个参数,ServletRequest封装的是请求数据,ServletResponse封装的是响应数据,后期我们可以通过这两个参数实现前后端的数据交互

小结

介绍完Servlet的执行流程,需要大家掌握两个问题:

  1. Servlet由谁创建?Servlet方法由谁调用?

Servlet由web服务器创建,Servlet方法由web服务器调用

  1. 服务器怎么知道Servlet中一定有service方法?

因为我们自定义的Servlet,必须实现Servlet接口并复写其方法,而Servlet接口中有service方法

4.4 生命周期

什么样的对象会研究其生命周期?

如果一个对象的创建、调用等管理过程不受人为控制,则我们需要研究其生命周期。

介绍完Servlet的执行流程后,我们知道Servlet是由Tomcat Web服务器帮我们创建的。

接下来咱们再来思考一个问题:Tomcat什么时候创建的Servlet对象?

要想回答上述问题,我们就需要对Servlet的生命周期进行一个学习。

  • 生命周期: 对象的生命周期指一个对象从被创建到被销毁的整个过程。

  • Servlet运行在Servlet容器(web服务器)中,其生命周期由容器来管理,分为4个阶段:

    1. 加载和实例化:默认情况下,当Servlet第一次被访问时,由容器创建Servlet对象(懒加载)Lazy Load
    默认情况,Servlet会在第一次访问被容器创建,但是如果创建Servlet比较耗时的话,那么第一个访问的人等待的时间就比较长,用户的体验就比较差,那么我们能不能把Servlet的创建放到服务器启动的时候来创建,具体如何来配置?
    
    @WebServlet(urlPatterns = "/demo1",loadOnStartup = 1)
    loadOnstartup的取值有两类情况
    	(1)负整数:第一次访问时创建Servlet对象
    	(2)0或正整数:服务器启动时创建Servlet对象,数字越小优先级越高
    
    1. 初始化:在Servlet实例化之后,容器将调用Servlet的init()方法初始化这个对象,完成一些如加载配置文件、创建连接等初始化的工作。该方法只调用一次。Servlet是单例对象

    2. 请求处理每次请求Servlet时,Servlet容器都会调用Servlet的==service()==方法对请求进行处理。每当tomcat调用共service方法时,都会开启一个新的线程执行内部的所有逻辑。

      service是多线程执行的,所以尽量不要在servlet的成员位置定义变量,然后在service中修改。

    3. 服务终止:当需要释放内存或者容器关闭时,容器就会调用Servlet实例的==destroy()==方法完成资源的释放。在destroy()方法调用之后,容器会释放这个Servlet实例,该实例随后会被Java的垃圾收集器所回收

  • 通过案例演示下上述的生命周期

    package com.itheima.web;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebServlet;
    import java.io.IOException;
    /**
    * Servlet生命周期方法
    */
    @WebServlet(urlPatterns = "/demo2",loadOnStartup = 1)
    public class ServletDemo2 implements Servlet {
    
        /**
         *  初始化方法
         *  1.调用时机:默认情况下,Servlet被第一次访问时,调用
         *      * loadOnStartup: 默认为-1,修改为0或者正整数,则会在服务器启动的时候,调用
         *  2.调用次数: 1次
         * @param config
         * @throws ServletException
         */
        public void init(ServletConfig config) throws ServletException {
            System.out.println("init...");
        }
    
        /**
         * 提供服务
         * 1.调用时机:每一次Servlet被访问时,调用
         * 2.调用次数: 多次
         * @param req
         * @param res
         * @throws ServletException
         * @throws IOException
         */
        public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
            System.out.println("servlet hello world~");
        }
    
        /**
         * 销毁方法
         * 1.调用时机:内存释放或者服务器关闭的时候,Servlet对象会被销毁,调用
         * 2.调用次数: 1次
         */
        public void destroy() {
            System.out.println("destroy...");
        }
        public ServletConfig getServletConfig() {
            return null;
        }
    
        public String getServletInfo() {
            return null;
        }
    
    
    }
    

    注意:如何才能让Servlet中的destroy方法被执行?

在Terminal命令行中,先使用mvn tomcat7:run启动,然后再使用ctrl+c关闭tomcat

小结

这节中需要掌握的内容是:

  1. Servlet对象在什么时候被创建的?

默认是第一次访问的时候被创建,可以使用@WebServlet(urlPatterns = “/demo2”,loadOnStartup = 1)的loadOnStartup 修改成在服务器启动的时候创建。

  1. Servlet生命周期中涉及到的三个方法,这三个方法是什么?什么时候被调用?调用几次?

涉及到三个方法,分别是 init()、service()、destroy()

init方法在Servlet对象被创建的时候执行,只执行1次

service方法在Servlet被访问的时候调用,每访问1次就调用1次

destroy方法在Servlet对象被销毁的时候调用,只执行1次

4.5 方法介绍

Servlet中总共有5个方法,我们已经介绍过其中的三个,剩下的两个方法作用分别是什么?

我们先来回顾下前面讲的三个方法,分别是:

  • 初始化方法,在Servlet被创建时执行,只执行一次
void init(ServletConfig config) 
  • 提供服务方法, 每次Servlet被访问,都会调用该方法
void service(ServletRequest req, ServletResponse res)
  • 销毁方法,当Servlet被销毁时,调用该方法。在内存释放或服务器关闭时销毁Servlet
void destroy() 

剩下的两个方法是:

  • 获取Servlet信息
String getServletInfo() 
//该方法用来返回Servlet的相关信息,没有什么太大的用处,一般我们返回一个空字符串即可
public String getServletInfo() {
    return "";
}
  • 获取ServletConfig对象
ServletConfig getServletConfig()

ServletConfig对象,在init方法的参数中有,而Tomcat Web服务器在创建Servlet对象的时候会调用init方法,必定会传入一个ServletConfig对象,我们只需要将服务器传过来的ServletConfig进行返回即可。具体如何操作?

package com.itheima.web;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;

/**
 * Servlet方法介绍
 */
@WebServlet(urlPatterns = "/demo3",loadOnStartup = 1)
public class ServletDemo3 implements Servlet {

    private ServletConfig servletConfig;
    /**
     *  初始化方法
     *  1.调用时机:默认情况下,Servlet被第一次访问时,调用
     *      * loadOnStartup: 默认为-1,修改为0或者正整数,则会在服务器启动的时候,调用
     *  2.调用次数: 1次
     * @param config
     * @throws ServletException
     */
    public void init(ServletConfig config) throws ServletException {
        this.servletConfig = config;
        System.out.println("init...");
    }
    public ServletConfig getServletConfig() {
        return servletConfig;
    }
    
    /**
     * 提供服务
     * 1.调用时机:每一次Servlet被访问时,调用
     * 2.调用次数: 多次
     * @param req
     * @param res
     * @throws ServletException
     * @throws IOException
     */
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println("servlet hello world~");
    }

    /**
     * 销毁方法
     * 1.调用时机:内存释放或者服务器关闭的时候,Servlet对象会被销毁,调用
     * 2.调用次数: 1次
     */
    public void destroy() {
        System.out.println("destroy...");
    }
    
    public String getServletInfo() {
        return "";
    }
}

getServletInfo()和getServletConfig()这两个方法使用的不是很多,大家了解下。

4.6 体系结构

通过上面的学习,我们知道要想编写一个Servlet就必须要实现Servlet接口,重写接口中的5个方法,虽然已经能完成要求,但是编写起来还是比较麻烦的,因为我们更关注的其实只有service方法,那有没有更简单方式来创建Servlet呢?

要想解决上面的问题,我们需要先对Servlet的体系结构进行下了解:

因为我们将来开发B/S架构的web项目,都是针对HTTP协议,所以我们自定义Servlet,会通过继承HttpServlet

具体的编写格式如下:

@WebServlet("/demo4")
public class ServletDemo4 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //TODO GET 请求方式处理逻辑
        System.out.println("get...");
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //TODO Post 请求方式处理逻辑
        System.out.println("post...");
    }
}
  • 要想发送一个GET请求,请求该Servlet,只需要通过浏览器发送http://localhost:8080/web-demo/demo4,就能看到doGet方法被执行了
  • 要想发送一个POST请求,请求该Servlet,单单通过浏览器是无法实现的,这个时候就需要编写一个form表单来发送请求,在webapp下创建一个a.html页面,内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/web-demo/demo4" method="post">
        <input name="username"/><input type="submit"/>
    </form>
</body>
</html>

启动测试,即可看到doPost方法被执行了。

Servlet的简化编写就介绍完了,接着需要思考两个问题:

  1. HttpServlet中为什么要根据请求方式的不同,调用不同的方法?
  2. 如何调用?

针对问题一,我们需要回顾之前的知识点前端发送GET和POST请求的时候,参数的位置不一致,GET请求参数在请求行中,POST请求参数在请求体中,为了能处理不同的请求方式,我们得在service方法中进行判断,然后写不同的业务处理,这样能实现,但是每个Servlet类中都将有相似的代码,针对这个问题,有什么可以优化的策略么?

package com.itheima.web;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


@WebServlet("/demo5")
public class ServletDemo5 implements Servlet {

    public void init(ServletConfig config) throws ServletException {

    }

    public ServletConfig getServletConfig() {
        return null;
    }

    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        //如何调用?
        //获取请求方式,根据不同的请求方式进行不同的业务处理
        HttpServletRequest request = (HttpServletRequest)req;
       //1. 获取请求方式
        String method = request.getMethod();
        //2. 判断
        if("GET".equals(method)){
            // get方式的处理逻辑
        }else if("POST".equals(method)){
            // post方式的处理逻辑
        }
    }

    public String getServletInfo() {
        return null;
    }

    public void destroy() {

    }
}

要解决上述问题,我们可以对Servlet接口进行继承封装,来简化代码开发。

package com.itheima.web;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class MyHttpServlet implements Servlet {
    public void init(ServletConfig config) throws ServletException {

    }

    public ServletConfig getServletConfig() {
        return null;
    }

    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest)req;
        //1. 获取请求方式
        String method = request.getMethod();
        //2. 判断
        if("GET".equals(method)){
            // get方式的处理逻辑
            doGet(req,res);
        }else if("POST".equals(method)){
            // post方式的处理逻辑
            doPost(req,res);
        }
    }

    protected void doPost(ServletRequest req, ServletResponse res) {
    }

    protected void doGet(ServletRequest req, ServletResponse res) {
    }

    public String getServletInfo() {
        return null;
    }

    public void destroy() {

    }
}

有了MyHttpServlet这个类,以后我们再编写Servlet类的时候,只需要继承MyHttpServlet,重写父类中的doGet和doPost方法,就可以用来处理GET和POST请求的业务逻辑。接下来,可以把ServletDemo5代码进行改造

@WebServlet("/demo5")
public class ServletDemo5 extends MyHttpServlet {

    @Override
    protected void doGet(ServletRequest req, ServletResponse res) {
        System.out.println("get...");
    }

    @Override
    protected void doPost(ServletRequest req, ServletResponse res) {
        System.out.println("post...");
    }
}

将来页面发送的是GET请求,则会进入到doGet方法中进行执行,如果是POST请求,则进入到doPost方法。这样代码在编写的时候就相对来说更加简单快捷。

类似MyHttpServlet这样的类Servlet中已经为我们提供好了,就是HttpServlet,翻开源码,大家可以搜索service()方法,你会发现HttpServlet做的事更多,不仅可以处理GET和POST还可以处理其他五种请求方式。

protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String method = req.getMethod();

        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);
            
        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);
            
        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);
            
        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);
            
        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);
            
        } else {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);
            
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }

小结

通过这一节的学习,要掌握:

  1. HttpServlet的使用步骤

继承HttpServlet

重写doGet和doPost方法

  1. HttpServlet原理

获取请求方式,并根据不同的请求方式,调用不同的doXxx方法

idea模板配置

在模板中,设置以下内容:

#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end
#parse("File Header.java")

#if ($JAVAEE_TYPE == "jakarta")
import jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;
#else
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
#end
import java.io.IOException;

@WebServlet("/${Entity_Name}")
public class ${Class_Name} extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
           this.doGet(request,response);
    }
}

4.7 urlPattern配置

Servlet类编写好后,要想被访问到,就需要配置其访问路径(urlPattern

  • 一个Servlet,可以配置多个urlPattern

    package com.itheima.web;
    
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.annotation.WebServlet;
    
    /**
    * urlPattern: 一个Servlet可以配置多个访问路径
    */
    @WebServlet(urlPatterns = {"/demo7","/demo8"})
    public class ServletDemo7 extends MyHttpServlet {
    
        @Override
        protected void doGet(ServletRequest req, ServletResponse res) {
            
            System.out.println("demo7 get...");
        }
        @Override
        protected void doPost(ServletRequest req, ServletResponse res) {
        }
    }
    

    在浏览器上输入http://localhost:8080/web-demo/demo7,http://localhost:8080/web-demo/demo8这两个地址都能访问到ServletDemo7的doGet方法。

  • urlPattern配置规则

    Servlet的映射路径

    • 完全路径匹配/精确匹配

      /aaa

      /Aaa 区分大小写

      /aaa/bbb/ccc 支持层级目录

      /**
       * UrlPattern:
       * * 精确匹配
       */
      @WebServlet(urlPatterns = "/user/select")
      public class ServletDemo8 extends MyHttpServlet {
      
          @Override
          protected void doGet(ServletRequest req, ServletResponse res) {
      
              System.out.println("demo8 get...");
          }
          @Override
          protected void doPost(ServletRequest req, ServletResponse res) {
          }
      }
      

      访问路径http://localhost:8080/web-demo/user/select

    • 目录匹配

      /aaa/* 路径只要以/aaa/开头即可。后面的*代表任意字符串

      /* 所有路径都会匹配

      package com.itheima.web;
      
      import javax.servlet.ServletRequest;
      import javax.servlet.ServletResponse;
      import javax.servlet.annotation.WebServlet;
      
      /**
       * UrlPattern:
       * * 目录匹配: /user/*
       */
      @WebServlet(urlPatterns = "/user/*")
      public class ServletDemo9 extends MyHttpServlet {
      
          @Override
          protected void doGet(ServletRequest req, ServletResponse res) {
      
              System.out.println("demo9 get...");
          }
          @Override
          protected void doPost(ServletRequest req, ServletResponse res) {
          }
      }
      

      访问路径http://localhost:8080/web-demo/user/任意

      思考:

      1. 访问路径http://localhost:8080/web-demo/user是否能访问到demo9的doGet方法?
      2. 访问路径http://localhost:8080/web-demo/user/a/b是否能访问到demo9的doGet方法?
      3. 访问路径http://localhost:8080/web-demo/user/select是否能访问到demo9还是demo8的doGet方法?

      答案是: 能、能、demo8,进而我们可以得到的结论是/user/*中的/*代表的是零或多个层级访问目录同时精确匹配优先级要高于目录匹配。

    • 扩展名匹配

      指定一个固定的扩展名,前面的字符串任意。

      不能以/开头编写。

      package com.itheima.web;
      
      import javax.servlet.ServletRequest;
      import javax.servlet.ServletResponse;
      import javax.servlet.annotation.WebServlet;
      
      /**
       * UrlPattern:
       * * 扩展名匹配: *.do
       */
      @WebServlet(urlPatterns = "*.do")
      public class ServletDemo10 extends MyHttpServlet {
      
          @Override
          protected void doGet(ServletRequest req, ServletResponse res) {
      
              System.out.println("demo10 get...");
          }
          @Override
          protected void doPost(ServletRequest req, ServletResponse res) {
          }
      }
      

      访问路径http://localhost:8080/web-demo/任意.do

      注意:

      1. 如果路径配置的不是扩展名,那么在路径的前面就必须要加/否则会报错

      1. 如果路径配置的是*.do,那么在*.do的前面不能加/,否则会报错

    • 任意匹配

      package com.itheima.web;
      
      import javax.servlet.ServletRequest;
      import javax.servlet.ServletResponse;
      import javax.servlet.annotation.WebServlet;
      
      /**
       * UrlPattern:
       * * 任意匹配: /
       */
      @WebServlet(urlPatterns = "/")
      public class ServletDemo11 extends MyHttpServlet {
      
          @Override
          protected void doGet(ServletRequest req, ServletResponse res) {
      
              System.out.println("demo11 get...");
          }
          @Override
          protected void doPost(ServletRequest req, ServletResponse res) {
          }
      }
      

      访问路径http://localhost:8080/demo-web/任意

      package com.itheima.web;
      
      import javax.servlet.ServletRequest;
      import javax.servlet.ServletResponse;
      import javax.servlet.annotation.WebServlet;
      
      /**
       * UrlPattern:
       * * 任意匹配: /*
       */
      @WebServlet(urlPatterns = "/*")
      public class ServletDemo12 extends MyHttpServlet {
      
          @Override
          protected void doGet(ServletRequest req, ServletResponse res) {
      
              System.out.println("demo12 get...");
          }
          @Override
          protected void doPost(ServletRequest req, ServletResponse res) {
          }
      }
      
      

      访问路径`http://localhost:8080/demo-web/任意

      / 叫做缺省匹配,默认匹配。default。如果有一个请求,请求的路径没有任何一个url-pattern与之匹配,也就是没有一个Servlet接受该请求。那么该请求就会给缺省路径的Servlet处理。所以这样的Servlet在一个项目中最多只能有一个。但是在Tomcat中,自带一个Servlet配置为了/。

      注意://*的区别?

      1. 当我们的项目中的Servlet配置了 “/”,会覆盖掉tomcat中的DefaultServlet,当其他的url-pattern都匹配不上时都会走这个Servlet

      2. 当我们的项目中配置了"/*",意味着匹配任意访问路径

      3. DefaultServlet是用来处理静态资源,如果配置了"/"会把默认的覆盖掉,就会引发请求静态资源的时候没有走默认的而是走了自定义的Servlet类,最终导致静态资源不能被访问

小结

  1. urlPattern总共有四种配置方式,分别是精确匹配、目录匹配、扩展名匹配、缺省匹配
  2. 五种配置的优先级为 精确匹配 > 目录匹配> 扩展名匹配 > 缺省匹配

4.8 XML配置

前面对应Servlet的配置,我们都使用的是@WebServlet,这个是Servlet从3.0版本后开始支持注解配置,3.0版本前只支持XML配置文件的配置方法。

对于XML的配置步骤有两步:

  • 编写Servlet类
package com.itheima.web;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;

public class ServletDemo13 extends MyHttpServlet {

    @Override
    protected void doGet(ServletRequest req, ServletResponse res) {

        System.out.println("demo13 get...");
    }
    @Override
    protected void doPost(ServletRequest req, ServletResponse res) {
    }
}
  • 在web.xml中配置该Servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    
    
    
    <!-- 
        Servlet 全类名
    -->
    <servlet>
        <!-- servlet的名称,名字任意-->
        <servlet-name>demo13</servlet-name>
        <!--servlet的类全名-->
        <servlet-class>com.itheima.web.ServletDemo13</servlet-class>
    </servlet>

    <!-- 
        Servlet 访问路径
    -->
    <servlet-mapping>
        <!-- servlet的名称,要和上面的名称一致-->
        <servlet-name>demo13</servlet-name>
        <!-- servlet的访问路径-->
        <url-pattern>/demo13</url-pattern>
    </servlet-mapping>
</web-app>

这种配置方式和注解比起来,确认麻烦很多,所以建议大家使用注解来开发。但是大家要认识上面这种配置方式,因为并不是所有的项目都是基于注解开发的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/372450.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

_react

React 中 keys 的作用是什么&#xff1f; Keys是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识 在开发过程中&#xff0c;我们需要保证某个元素的 key 在其同级元素中具有唯一性。在 React Diff 算法中React 会借助元素的 Key 值来判断该元素是新近创建的还…

ADAS-可见光相机之Cmos Image Sensor

引言 “ 可见光相机在日常生活、工业生产、智能制造等应用有着重要的作用。在ADAS中更是扮演着重要的角色&#xff0c;如tesla model系列全车身10多个相机&#xff0c;不断感知周围世界。本文着重讲解下可见光相机中的CIS(CMOS Image Sensor)。” 定义 光是一种电磁波&…

如何在 iPhone 上恢复已删除的通话记录/通话记录

您的通话记录/通话记录可能很重要&#xff0c;尤其是当您想要拨打之前联系过但未保存的号码时。如果您碰巧删除了通话记录&#xff08;有意或无意&#xff09;&#xff0c;本指南将帮助您了解如何检索它们并找回您需要使用的所有记录。我们将根据您的情况和您拥有的工具讨论不同…

【Tomcat 】Tomcat 架构原理解析到架构设计借鉴

Tomcat 发展这么多年&#xff0c;已经比较成熟稳定。在如今『追新求快』的时代&#xff0c;Tomcat 作为 Java Web 开发必备的工具似乎变成了『熟悉的陌生人』&#xff0c;难道说如今就没有必要深入学习它了么&#xff1f;学习它我们又有什么收获呢&#xff1f; 静下心来&#…

一文让你彻底理解关于消息队列的使用

一、消息队列概述 消息队列中间件是分布式系统中重要的组件&#xff0c;主要解决应用解耦&#xff0c;异步消息&#xff0c;流量削锋等问题&#xff0c;实现高性能&#xff0c;高可用&#xff0c;可伸缩和最终一致性架构。目前使用较多的消息队列有ActiveMQ&#xff0c;Rabbit…

Spring Security认证研究

1.项目中认证的三种方式&#xff1a; 1.统一认证 认证通过由认证服务向给用户颁发令牌&#xff0c;相当于访问系统的通行证&#xff0c;用户拿着令牌去访问系统的资源。 2.单点登录&#xff0c;对于微服务项目&#xff0c;因为包含多个模块&#xff0c;所以单点登录就是使得用户…

烙铁使用方法

烙铁使用 烙铁是硬件工程师最经常使用的工具之一,一把性能保持良好的烙铁能帮助我们快速进行电路调试。烙铁第一次加热时采用焊锡均匀涂覆在烙铁头上,以便去除包在烙铁头上面的氧化物。在工作中我们需要根据情况选择合适的烙铁头类型,合适的温度进行操作。完成焊接后要在烙铁…

TypeError: load() missing 1 required positional argument: ‘Loader‘解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。喜欢通过博客创作的方式对所学的知识进行总结与归纳,不仅形成深入且独到的理…

BLIP2-图像文本预训练

文章目录摘要解决问题算法模型结构通过frozen图像编码器学习视觉语言表征图像文本对比学习&#xff08;ITC&#xff09;基于图像文本生成&#xff08;ITG&#xff09;图文匹配&#xff08;ITM&#xff09;从大规模语言模型学习视觉到语言生成模型预训练预训练数据预训练图像编码…

基础夯实,字节内部总结240道算法LeetCode刷题笔记,直呼太全

1、什么是算法算法(algorithm&#xff0c;[ˈlɡərɪəm]&#xff0c;计算程序)&#xff1a;就是定义良好的计算过程&#xff0c;他取一个或一组的值为输入&#xff0c;并产生出一个或一组值作为输出。简单来说算法就是一系列的计算步骤&#xff0c;用来将输入数据转化成输出结…

从Linux源码角度看套接字的Listen及连接队列

今天就从Linux源码的角度看下Server端的Socket在进行listen的时候到底做了哪些事情(基于Linux 3.10内核)&#xff0c;当然由于listen的backlog参数和半连接hash表以及全连接队列都相关&#xff0c;在这里也一块讲了。 Server端Socket需要Listen 众所周知&#xff0c;一个Serv…

archlinux docker配置php5.3

一直在维护一下10年前的老项目&#xff0c;是基于php5.3开发的。一直在使用windows虚拟机在开发php5.3。最近就想尝试在archlinux主系统中开发。 什么是php PHP 是一种广泛使用的通用脚本语言&#xff0c;特别适合 Web 开发&#xff0c;可以嵌入到 HTML 中。 AUR安装php5.3 …

【C语言进阶】了解计算机的程序环境和预处理过程 掌握计算机预处理操作

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;C语言进阶 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录1.编译与链接1.1 程…

WebGPU学习(4)---使用 UniformBuffer

接下来让我们使用 UniformBuffer。UniformBuffer 是一个只读内存区域&#xff0c;可以在着色器上访问。 这次&#xff0c;我们将传递给着色器的矩阵存储在 UniformBuffer 中。演示示例 1.在顶点着色器中的 UniformBuffer 这次我们在顶点着色器里定义一个名为Uniforms的新结构体…

TCP 的演化史-sack 与 reordering metric

就着 TCP 本身说事&#xff0c;而不是高谈阔论关于它是如何不合时宜&#xff0c;然后摆出一个更务虚的更新。 从一个 case 开始。 按照现在 Linux TCP(遵守 RFC) 实现&#xff0c;以下是一个将会导致 reordering 更新的 sack 序列&#xff1a; 考虑一种情况&#xff0c;这两个…

【Spring】谈谈你对IOC和AOP理解(2023最新)

目录一.IOC(Inversion of Control)1.IOC是什么&#xff1f;2.IOC的实现原理二.AOP(Aspect Oriented Programming)1.AOP是什么&#xff1f;2.AOP的实现原理3.说一下AOP都有哪些基本理念&#xff1f;或者是AOP的术语4.Advice(通知)的类型有哪些5.AOP的应用场景6.使用AOP实例(日志…

jvisualvm远程监控Java程序

启用远程监控&#xff1a; 方式一&#xff1a;启动参数进行配置 启动远程应用需指定jmx相关配置 java -jar -Djava.rmi.server.hostname远程服务ip -Dcom.sun.management.jmxremote.port18888 -Dcom.sun.management.jmxremotetrue -Dcom.sun.management.jmxremote.sslfa…

运动耳机推荐、最值得入手的运动耳机清单共享

现在市面上各式各样的运动蓝牙耳机着实让人挑花了眼,怎样才能从纷繁复杂的市场中挑选出专业性、安全性、舒适性等各个方面都做地可圈可点的运动蓝牙耳机可真不是一件易事啊&#xff0c;甚至连不少老朋友都会踩坑&#xff0c;为了能让大家挑到真正的运动蓝牙耳机&#xff0c;为此…

Dev C++ 调试功能详细总结

原文链接&#xff1a; Dev C 调试功能详细总结https://mp.weixin.qq.com/s/H9VwLNcJ0tY3j3265R0_7Q 大家好&#xff0c;我是CodeAllen&#xff08;康哥&#xff09;&#xff0c;今天是2023年2月25日&#xff0c;继上一篇介绍了我在Windows端经常用来验证代码的工具Dev C的基本…

pytest测试框架——pytest.ini用法

这里写目录标题一、pytest用法总结二、pytest.ini是什么三、改变运行规则pytest.inicheck_demo.py执行测试用例四、添加默认参数五、指定执行目录六、日志配置七、pytest插件分类八、pytest常用插件九、改变测试用例的执行顺序十、pytest并行与分布式执行十一、pytest内置插件h…