Mushroom Notes Mushroom Notes
🍄首页
  • JavaSE

    • 基础篇
    • 数据结构
    • IO流
    • Stream流
    • 函数式接口
    • JUC
    • 反射
    • 网络编程
    • 设计模式
  • JavaEE

    • Servlet
    • JDBC
    • 会话技术
    • 过滤器监听器
    • 三层架构
  • JDK

    • 总览
  • JVM

    • 总览
  • 常用mate
  • CSS
  • JavaScript
  • rds 数据库

    • MySQL
    • MySQL 进阶
    • MySQL 库表规范
  • nosql 数据库

    • Redis
    • Redis 进阶
    • Redis 底层
    • MongoDB
  • Spring生态

    • Spring
    • Spring MVC
    • Spring boot
    • Spring Validation
  • Spring Cloud生态

    • Spring Cloud
    • 服务治理
    • 远程调用
    • 网关路由
    • 服务保护
    • 分布式事务
    • 消息中间件
  • 数据库

    • Mybatis
    • Mybatis Plus
    • Elasticsearch
    • Redisson
  • 通信

    • Netty
📚技术
  • 方案专题
  • 算法专题
  • BUG专题
  • 安装专题
  • 网安专题
  • 面试专题
  • 常用网站
  • 后端常用
  • 前端常用
  • 分类
  • 标签
  • 归档

kinoko

一位兴趣使然的热心码农
🍄首页
  • JavaSE

    • 基础篇
    • 数据结构
    • IO流
    • Stream流
    • 函数式接口
    • JUC
    • 反射
    • 网络编程
    • 设计模式
  • JavaEE

    • Servlet
    • JDBC
    • 会话技术
    • 过滤器监听器
    • 三层架构
  • JDK

    • 总览
  • JVM

    • 总览
  • 常用mate
  • CSS
  • JavaScript
  • rds 数据库

    • MySQL
    • MySQL 进阶
    • MySQL 库表规范
  • nosql 数据库

    • Redis
    • Redis 进阶
    • Redis 底层
    • MongoDB
  • Spring生态

    • Spring
    • Spring MVC
    • Spring boot
    • Spring Validation
  • Spring Cloud生态

    • Spring Cloud
    • 服务治理
    • 远程调用
    • 网关路由
    • 服务保护
    • 分布式事务
    • 消息中间件
  • 数据库

    • Mybatis
    • Mybatis Plus
    • Elasticsearch
    • Redisson
  • 通信

    • Netty
📚技术
  • 方案专题
  • 算法专题
  • BUG专题
  • 安装专题
  • 网安专题
  • 面试专题
  • 常用网站
  • 后端常用
  • 前端常用
  • 分类
  • 标签
  • 归档
  • JavaSE

  • JavaEE

    • Servlet
      • HTTP
        • 请求数据格式
        • 响应数据格式
        • 响应状态码
      • Tomcat
        • Tomact相关概念
        • Tomcat的相关操作
        • Web项目
        • Tomcat Maven插件
      • Servlet
        • 简介
        • 使用步骤
        • 执行流程
        • 生命周期
        • Servlet体系
        • HttpServlet底层原理
        • urlPattern配置
        • XML配置
        • 整合多个Servlet
      • 请求响应流程
      • Request
        • Request继承体系
        • Request获取请求数据
        • 通用获取参数方式
        • 参数乱码问题
        • Request请求转发
      • Response
        • Response继承体系
        • Response响应数据
        • Response重定向
        • 路径问题
        • 请求转发和重定向的使用场景
    • JDBC
    • 会话技术
    • Filter&Listener
    • 三层架构
  • JDK版本特性

  • JVM

  • Java
  • JavaEE
kinoko
2023-12-17
目录

Servlet

# HTTP


简介

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

  • HTML超文本标记语言,可以说HTTP就是传输HTML的协议
  • 数据传输的规则指的是请求数据和响应数据需要按照指定的格式进行传输。
  • 如果想知道具体的格式,可以打开浏览器,点击F12打开开发者工具,点击Network来查看某一次请求的请求数据和响应数据具体的格式内容

HTTP协议特点

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

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

TCP更加底层,TCP是传输层协议,HTTP是应用层的协议

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

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

    • 无状态指的是客户端发送HTTP请求给服务端之后,服务端根据请求响应数据,响应完后,不会记录任何信息。这种特性有优点也有缺点:
      • 缺点:多次请求间不能共享数据。Java中使用会话技术(Session,Cookice)解决数据共享问题
      • 优点:速度快

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

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

# 请求数据格式


请求数据总共分为三部分内容,分别是请求行、请求头、请求体,请求方式有七种,最常用的是GET和POST

GET /login?username=lisi&password=123 HTTP/1.1 		//请求行

// 请求头		
Host: www.itcast.cn 
Connection: keep-alive 
Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 
User-Agent: Mozilla/5.0 Chrome/91.0.4472.106
…
1
2
3
4
5
6
7
8

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

请求头: 第二行开始,格式为key: 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 /login HTTP/1.1		// 请求行

// 请求头
Host: www.itcast.cn 
Connection: keep-alive 
Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 
User-Agent: Mozilla/5.0 Chrome/91.0.4472.106

username=lisi&password=123		//请求体
1
2
3
4
5
6
7
8
9

GET请求和 POST请求区别:

  • GET请求请求参数在请求行中,没有请求体。POST请求请求参数在请求体中
  • GET请求请求参数大小有限制,一般不会超过1KB,POST没有

# 响应数据格式


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

HTTP/1.1 200 OK		// 响应行

// 响应头
Server: Tengine
Content-Type: text/html
Transfer-Encoding: chunked…

// 响应体
<html>
<head>
     <title></title>
</head>
<body></body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

响应行:响应数据的第一行,响应行包含三块内容,分别是
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秒

响应体: 最后一部分。存放响应数据

# 响应状态码

状态码大类

状态码分类 说明
1xx 响应中——临时状态码,表示请求已经接受,告诉客户端应该继续请求或者如果它已经完成则忽略它
2xx 成功——表示请求已经被成功接收,处理已完成
3xx 重定向——重定向到其它地方:它让客户端再发起一个请求以完成整个处理。
4xx 客户端错误——处理发生错误,责任在客户端,如:客户端的请求一个不存在的资源,客户端未被授权,禁止访问等
5xx 服务器端错误——处理发生错误,责任在服务端,如:服务端抛出异常,路由出错,HTTP版本不支持等

状态码大全:https://cloud.tencent.com/developer/chapter/13553 (opens new window)

常见的响应状态码

状态码 英文描述 解释
200 OK 客户端请求成功,即处理成功,这是我们最想看到的状态码
302 Found 指示所请求的资源已移动到由Location
响应头给定的 URL,浏览器会自动重新访问到这个页面
304 Not Modified 告诉客户端,你请求的资源至上次取得后,服务端并未更改,你直接用你本地缓存吧。隐式重定向
400 Bad Request 客户端请求有语法错误,不能被服务器所理解
403 Forbidden 服务器收到请求,但是拒绝提供服务,比如:没有权限访问相关资源
404 Not Found 请求资源不存在,一般是URL输入有误,或者网站资源被删除了
428 Precondition Required 服务器要求有条件的请求,告诉客户端要想访问该资源,必须携带特定的请求头
429 Too Many Requests 太多请求,可以限制客户端请求某个资源的数量,配合 Retry-After(多长时间后可以请求)响应头一起使用
431 Request Header Fields Too Large 请求头太大,服务器不愿意处理请求,因为它的头部字段太大。请求可以在减少请求头域的大小后重新提交。
405 Method Not Allowed 请求方式有误,比如应该用GET请求方式的资源,用了POST
500 Internal Server Error 服务器发生不可预期的错误。服务器出异常了,赶紧看日志去吧
503 Service Unavailable 服务器尚未准备好处理请求,服务器刚刚启动,还未初始化好
511 Network Authentication Required 客户端需要进行身份验证才能获得网络访问权限

# Tomcat


Web服务器简介

Web服务器是一个应该程序(软件),对HTTP协议的操作进行封装,使得程序员不必直接对协议进行操作,让Web开发更加便捷。主要功能是"提供网上信息浏览服务"。
image.png
Web服务器是安装在服务器端的一款软件,将来我们把自己写的Web项目部署到Web Tomcat服务器软件中,当Web服务器软件启动后,部署在Web服务器软件中的页面就可以直接通过浏览器来访问了。

Web服务器软件使用步骤

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

# Tomact相关概念


概念

  • Tomcat是Apache软件基金会一个核心项目,是一个开源免费的轻量级Web服务器,支持Servlet/JSP少量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/ (opens new window) 从官网上可以下载对应的版本进行使用。

目录结构
image.png

补充:bin目录下有两类文件,一种是以.bat结尾的,是Windows系统的可执行文件,一种是以.sh结尾的,是Linux系统的可执行文件。webapps就是以后项目部署的目录。

# Tomcat的相关操作


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

注意:启动的过程中,控制台有中文乱码,需要修改conf/logging.prooperties
java.util.logging.ConsoleHandler.encoding = UTF-8 改为 GBK

关闭:

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

配置

修改端口:Tomcat默认的端口是8080,要想修改Tomcat启动的端口号,需要修改 conf/server.xml约69行处

<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
1
2
3

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

启动时可能出现的错误

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

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

部署:
有三种方式。1、直接将项目复制到webapps目录下 2、采用war包的方式 3、xml配置方式

**注意:**war包的方式需要将整个项目使用压缩工具打包成一个zip文件,然后将文件的扩展名改为war,并且压缩包中的文件不能有中文名。

xml配置方式:
找到conf/Catalina/localhost文件夹,创建一个随意的xml文件,内容为:<Context docBase="资源路径"/>
之后就可以直接通过Tomcat端口访问了,访问路径:localhost:8080/xml文件名/资源名称

Tomcat首页访问配置

在web.xml文件中加上如下配置

<welcome-file-list>
    <welcome-file>资源名称</welcome-file>
</welcome-file-list>
1
2
3

# Web项目


Web项目结构

Web项目的结构分为:开发中的项目和开发完可以部署的Web项目

Maven Web项目结构: 开发中的项目
image.png

开发完成部署的Web项目
image.png

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

# Tomcat Maven插件

在IDEA中使用本地Tomcat进行项目部署,需要在pom.xml中添加Tomcat插件

<build>
    <plugins>
    	<!--Tomcat插件 -->
        <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <version>2.2</version>
        </plugin>
    </plugins>
</build>
1
2
3
4
5
6
7
8
9
10

使用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	
								-->
                <path>/</path>
            </configuration>
        </plugin>
    </plugins>
</build>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# Servlet


# 简介

Servlet是JavaWeb最为核心的内容,它是Java提供的一门动态web资源开发技术。使用Servlet就可以实现,根据不同的登录用户在页面上动态显示不同内容。Servlet是JavaEE规范之一,其实就是一个接口,将来我们需要定义Servlet类实现Servlet接口,并由web服务器运行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
2
3
4
5
6
7
8
9
10
11
12
  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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  1. 配置:在类上使用@WebServlet注解,配置该Servlet的访问路径
@WebServlet("/demo1")
1
  1. 访问:启动Tomcat,浏览器中输入URL地址访问该Servlet
http://localhost:8080/web-demo/demo1
1
  1. 器访问后,在控制台会打印servlet hello world~ 说明servlet程序已经成功运行。

# 执行流程

浏览器发出http://localhost:8080/web-demo/demo1请求,从请求中可以解析出三部分内容,分别是localhost:8080、web-demo、demo1

  • 根据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运行在Servlet容器(web服务器)中,其生命周期由容器来管理,分为4个阶段:

  1. 加载和实例化:默认情况下,当Servlet第一次被访问时,由容器创建Servlet对象

默认情况,Servlet会在第一次访问被容器创建,若想在服务器启动时创建可以在@WebServlet注解中添加属性

@WebServlet(urlPatterns = "/demo1",loadOnStartup = 1)
  
loadOnstartup的取值有两类情况
	(1)负整数:第一次访问时创建Servlet对象
	(2)0或正整数:服务器启动时创建Servlet对象,数字越小优先级越高
1
2
3
4
5
  1. 初始化:在Servlet实例化之后,容器将调用Servlet实例的init()方法初始化这个对象,完成一些如加载配置文件、创建连接等初始化工作。该方法只调用一次调用一次
  2. 请求处理:每次请求Servlet时,Servlet容器都会调用Servlet实例的service()方法对请求进行处理
  3. 服务终止:当需要释放内存或者容器关闭时,容器就会调用Servlet实例的destroy()方法完成资源的释放。在destroy()方法调用之后,容器会释放这个Servlet实例,该实例随后会被Java的垃圾收集器回收。

**补充:**Servlet接口还有两个方法

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

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

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

    private ServletConfig servletConfig;
    
    public void init(ServletConfig config) throws ServletException {
        this.servletConfig = config;
        System.out.println("init...");
    }
    public ServletConfig getServletConfig() {
        return servletConfig;
    }
    
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

loadOnStartup = 1 :在服务器启动时创建Servlet实例。小于0为不创建,大于0,数字越小优先级越高

# 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...");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# HttpServlet底层原理

public abstract class HttpServlet extends GenericServlet {
    // GenericServlet实现Servlet
    
    // 调用Servlet的service
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        if (req instanceof HttpServletRequest && res instanceof HttpServletResponse) {
            
            // 进行一个向下转型
            HttpServletRequest request = (HttpServletRequest)req;
            HttpServletResponse response = (HttpServletResponse)res;
            
           	// 调用自身的service
            this.service(request, response);
        } else {
            throw new ServletException("non-HTTP request or response");
        }
    }
    
    // 仅截取了关键代码
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 获取请求方式
        String method = req.getMethod();
        // 是get则走doGet,是post则走doPost
        if (method.equals("GET")) {
            this.doGet(req, resp);
        } else if (method.equals("POST")) {
            this.doPost(req, resp);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# urlPattern配置


urlPattern是@WebServlet的属性
一个Servlet,可以配置多个urlPattern:如@WebServlet(urlPatterns = {"/demo7","/demo8"})

urlPattern和valuse属性都是用来配置Servlet路径的

urlPattern匹配规则:

精确匹配

  • 配置路径:@WebServlet(urlPatterns = {"/user/select"})
  • 访问路径:localhost:8080/web-demo/user/select

目录匹配

  • 配置路径:@WebServlet(urlPatterns = {"/user/*"})

  • 访问路径:localhost:8080/web-demo/user/aaa

    localhost:8080/web-demo/user/bbb

/*代表的是零个或多个层级访问,意思是该文件夹下的所有目录

扩展名匹配

  • 配置路径:@WebServlet(urlPatterns = {"*.do"})

  • 访问路径:localhost:8080/web-demo/user/aaa.do

    localhost:8080/web-demo/user/bbb.do

注意:如果路径配置不是扩展名匹配,则必须在路径前加上/否则会报错,并且前缀匹配和后缀匹配不能同时出现在一个地址中,否则会出现异常:

Caused by: 
	java.lang.IllegalArgumentException:
		Invalid <url-pattern> [/*.do] in servlet mapping
1
2
3

因为这种格式是错误的,会导致tomcat加载当前项目失败,整个项目中所有的资源都不能访问。

任意匹配

  • 配置路径:@WebServlet(urlPatterns = {"/"})

    @WebServlet(urlPatterns = {"/*"})

  • 访问路径:localhost:8080/web-demo/user/hehe

    localhost:8080/web-demo/user/haha

补充:/和/*的区别

  1. 当我们的项目中的Servlet配置了 "/",会覆盖掉tomcat中的DefaultServlet,当其他的url-pattern都匹配不上时都会走这个Servlet
  2. 当我们的项目中配置了"/*",意味着匹配任意访问路径
  3. DefaultServlet是用来处理静态资源,如果配置了"/"会把默认的覆盖掉,就会引发请求静态资源的时候没有走默认的静态资源而是走了自定义的Servlet类,最终导致静态资源不能被访问

五种配置的优先级为 精确匹配 > 目录匹配> /* > / >扩展名匹配

# XML配置


注解是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) {
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  • 在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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 整合多个Servlet


若业务繁琐,会导致需要定义很多个servlet,所以可以将多个servlet整合到一个servlet中,使得项目结构变得简介,并且减少servlet对象的创建。

定义一个多个Servlet的超级父类

public class BaseServlet extends HttpServlet {

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

        // 获取字节码对象
        Class<? extends Servlet> spServlet = this.getClass();

        // 截取出访问的子地址作为方法名
        String uri = req.getRequestURI();
        int index = uri.lastIndexOf("/");
        String subUri = uri.substring(index + 1);

        // 调用方法
        try {
            Method method = spServlet.getDeclaredMethod(subUri);
            method.invoke(this,req,resp);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

一切共性或者说重复的代码块都可以添加到父类中

普通Servlet继承超级父类,编写业务代码

@WebServlet("/brand/*")
public class BrandServlet extends BaseServlet {

    private BrandService brandService = new BrandService;

    public void add(){
        System.out.println("添加方法...");
    }

    public void select(){
        System.out.println("查询方法...");
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 请求响应流程

image.png
**request:**获取请求数据

  • 浏览器会发送HTTP请求到后台服务器[Tomcat]
  • HTTP的请求中会包含很多请求数据[请求行+请求头+请求体]
  • 后台服务器[Tomcat]会对HTTP请求中的数据进行解析并把解析结果存入到一个对象中
  • 所存入的对象即为request对象,所以我们可以从request对象中获取请求的相关参数
  • 获取到数据后就可以继续后续的业务,比如获取用户名和密码就可以实现登录操作的相关业务

response: 设置响应数据

  • 业务处理完后,后台就需要给前端返回业务处理的结果即响应数据
  • 把响应数据封装到response对象中
  • 后台服务器[Tomcat]会解析response对象,按照[响应行+响应头+响应体]格式拼接结果
  • 浏览器最终解析结果,把内容展示在浏览器给用户浏览

# Request

# Request继承体系


image.png

ServletRequest和HttpServletRequest都是接口,RequestFacade是两者的实现类,由Tomcat实现

# Request获取请求数据


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

请求行:GET /request-response-demo/req1?username=zhangsan HTTP/1.1

String getMethod():获取请求方式: GET
String getContextPath():获取虚拟目录(项目访问路径): /request-response-demo
StringBuffer getRequestURL(): 获取URL(统一资源定位符):http://localhost:8080/request-response-demo/req1
String getRequestURI():获取URI(统一资源标识符): /request-response-demo/req1
String getQueryString():获取请求参数(GET方式): username=zhangsan&password=123

请求头:User-Agent: Mozilla/5.0 Chrome/91.0.4472.106

String getHeader(String name):根据请求头名称,获取值

请求体:username=superbaby&password=123

ServletInputStream getInputStream():获取字节输入流
BufferedReader getReader():获取字符输入流

使用流对象的readLine()读取请求体

# 通用获取参数方式


由于GET方式提交的参数在请求行,POST方式提交的参数在请求体,并且在请求行与请求体获取参数的方式是不同的,于是Tomcat的RequestFacade提供了通用的获取参数方法:

方法 说明
Map<String, String[]> getParameterMap() 获取所有参数Map集合
String getParameter(String name) 根据名称获取参数值(单个值)
String[] getParameterValues(String name) 根据名称获取参数值(数组)

# 参数乱码问题


在获取中文字符时,存在乱码问题。

POST请求解决方案

分析出现中文乱码的原因:

  • POST的请求参数是通过request的getReader()来获取流中的数据
  • TOMCAT在获取流的时候采用的编码是ISO-8859-1,也就是使用ISO-8859-1解码
  • ISO-8859-1编码是不支持中文的,所以会出现乱码

解决方案:

  • 页面设置的编码格式为UTF-8
  • 把TOMCAT在获取流数据之前的编码设置为UTF-8
  • 通过request.setCharacterEncoding("UTF-8")设置编码,UTF-8也可以写成小写

GET请求解决方案

分析出现中文乱码的原因:

  • 浏览器通过HTTP协议发送请求和数据给后台服务器(Tomcat)
  • 浏览器在发送HTTP的过程中会对中文数据进行URL编码
  • 在进行URL编码的时候会采用页面标签指定的UTF-8的方式进行编码,张三编码后的结果为%E5%BC%A0%E4%B8%89
  • 后台服务器(Tomcat)接收到%E5%BC%A0%E4%B8%89后会默认按照ISO-8859-1进行URL解码
  • 由于前后编码与解码采用的格式不一样,就会导致后台获取到的数据为乱码。

【扩展】Java中的URL编码相关API 编码:java.net.URLEncoder.encode("需要被编码的内容","字符集(UTF-8)") 解码:java.net.URLDecoder.decode("需要被解码的内容","字符集(UTF-8)")

解决方案: 获取参数后进行一个转码,先解码再编码String(username.getBytes("ISO_8859_1"),"UTF_8");

Tomcat 8.0 之后,已将GET请求乱码问题解决,设置默认的解码方式为UTF-8

# Request请求转发


请求转发(forward): 一种在服务器内部的资源跳转方式。
image.png

  1. 浏览器发送请求给服务器,服务器中对应的资源A接收到请求
  2. 资源A处理完请求后将请求发给资源B
  3. 资源B处理完后将结果响应给浏览器
  4. 请求从资源A到资源B的过程就叫请求转发

实现方式:req.getRequestDispatcher("/资源B路径").forward(req,resp);

**请求转发资源间共享数据:**使用Request对象

方法 说明
void setAttribute(String name, Object o) 存储数据到 request域中
Object getAttribute(String name) 根据 key,从request域中获取值
void removeAttribute(String name) 根据 key,从request域中删除该键值对

请求转发特点:

  • 浏览器地址栏路径不发生变化
  • 只能转发到当前服务器的内部资源
  • 一次请求,可以在转发的资源间使用request共享数据

# Response

# Response继承体系


image.png

ServletResponse和HttpServletResponse都是接口,ResponseFacade是两者的实现类,由Tomcat实现

# Response响应数据


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

响应行:HTTP/1.1 200 OK

void setStatus(int sc):设置响应状态码

响应头:Content-Type: text/html

void setHeader(String name, String value) :设置响应头键值对

响应体:<html><head>head><body></body></html>

PrintWriter getWriter():获取字符输出流
ServletOutputStream getOutputStream():获取字节输出流

字符数据响应示例

PrintWriter writer = resp.getWriter();	// 获取Response对象字符输出流
writer.write("aaa"); // 写数据
1
2

•该流不需要关闭,随着响应结束,response对象销毁,由服务器关闭 •中文数据乱码:原因通过Response获取的字符输出流默认编码:ISO-8859-1 解决方案:设置输出流编码,并告诉浏览器 resp.setContentType("text/html;charset=utf-8"); 【注意】若浏览器无法识别响应类型,会弹出下载框

字节数据响应示例

ServletOutputStream outputStream = resp.getOutputStream();
outputStream.write(字节数据);

// 推荐使用commons-io
IOUtils.copy(输入流,输出流);
1
2
3
4
5

commons-io坐标 <dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>

【补充】获取Tomact的部署路径:getServletContext().getRealPath("webapps下的资源路径")

# Response重定向


重定向(Redirect):一种资源跳转方式

流程图
image.png
实现方式:

resp.setStatus(302);	//设置响应状态码为302
resp.setHeader("location","资源B的访问路径");	//添加location=/request-demo/resp2的头信息
1
2

ResponseFacade提供了一个一步到位的方法实现重定向:resposne.sendRedirect("资源B访问路径")

重定向特点:

  • 浏览器地址栏路径发生变化
  • 可以重定向到任意位置的资源(服务器内部、外部均可)
  • 两次请求,不能在多个资源使用request共享数据

# 路径问题

明确路径谁使用:(重定向时是浏览器使用路径,转发时是服务器使用)

Servlet配置时: 必须写/,这是规定。

浏览器使用:(表单提交到demo01)
加/是绝对路径,会缺少项目访问路径
image.png
不加/是相对路径,不会缺少项目访问路径
image.png

服务器使用: 加不加/都可以

结论: 浏览器使用时不加/,服务器使用加不加都可以

idea部署Tomact时若将Deploymentd的Application context设置为/,访问路径会自动省略项目访问路径 重定向属于浏览器使用,不加/

# 请求转发和重定向的使用场景


  • 跳转到下一个页面,不需要传递数据,建议使用重定向
  • 跳转到下一个页面,并且要把数据传递给下一个页面,转发
  • 跳转到下一个页面,并且将来的数据要给下一个页面及其他很多页面,重定向+session
#java#servlet
上次更新: 2023/12/29 11:32:56
设计模式
JDBC

← 设计模式 JDBC→

最近更新
01
JVM 底层
09-13
02
JVM 理论
09-13
03
JVM 应用
09-13
更多文章>
Theme by Vdoing | Copyright © 2022-2024 kinoko | MIT License | 粤ICP备2024165634号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式