JavaWeb(一) | 基本概念(web服务器、Tomcat、HTTP、Maven)、Servlet 简介

news2024/12/29 9:02:51

1. 基本概念

1.1、前言

web开发:

  • web,网页的意思,www.baidu.com·
  • 静态 web
    • html,css
    • 提供给所有人看的数据始终不会发生变化!
  • 动态 web
    • 淘宝,几乎是所有的网站;
    • 提供给所有人看的数据始终会发生变化,每个人在不同的时间,不同的地点看到的信息各不相同!
    • 技术栈:Servlet/JSP,ASP,PHP

1.2、web应用程序

可以提供浏览器访问的程序;

  • a.html、b.html.…. 多个 web 资源,这些 web 资源可以被外界访问,对外界提供服务;
  • 你们能访问到的任何一个页面或者资源,都存在于这个世界的某一个角落的计算机上。
  • URL:通过这个 URL 可以访问到某个电脑上的资源
  • 这个统一的web资源会被放在同一个文件夹下,web应用程序 需要依赖 Tomcat:服务器 来展示
  • 一个web应用由多部分组成(静态web,动态web)
    • html,css,js
    • jsp,servlet
    • Java程序
    • jar包
    • 配置文件(Properties)

Web应用程序编写完毕后,若想提供给外界访问;需要一个服务器来统一管理

1.3、静态web

  • *.htm, *.html 这些都是网页的后缀、如果服务器上一直存在这些东西,我们就可以直接进行读取(需要网络);

下面是静态 web 的流程
在这里插入图片描述

  • 静态web存在的缺点
    • Web页面无法动态更新,所有用户看到都是同一个页面
      • 轮播图,点击特效:伪动态,下面是让静态页面可以动起来的两种技术
      • JavaScript [实际开发中,它用的最多]
      • VBScript
    • 它无法和数据库交互(数据无法持久化,用户无法交互)

1.4、动态web

页面会动态展示,“web 页面的展示效果因人而异”

WebServer Plugin 一个处理静态资源,一个处理动态资源,静态资源就是上面那条路,先走 WebServer,去 File System 获取静态资源。动态资源会去请求动态资源 JSP/Servlet,请求完了交给 WebServer
在这里插入图片描述

缺点:

  • 加入服务器的动态web资源出现了错误,我们需要重新编写我们的后台程序,重新发布;
  • 重新发布就需要停机维护

优点:

  • Web页面可以动态更新,所有用户看到都不是同一个页面
  • 它可以与数据库交互(数据持久化:注册,商品信息,用户信息………)
    在这里插入图片描述

2、web服务器

2.1、技术讲解

ASP:

  • 微软:国内最早流行的就是 ASP;
  • 在HTML中嵌入了VB的脚本,ASP+COM;
  • 在ASP开发中,基本一个页面都有几千行的业务代码,页面极其混乱 维护成本高!
  • C# 语言开发
  • 使用的是 IIS 服务器

php:

  • PHP开发速度很快,功能很强大,跨平台,代码很简单(中国 70% 的网站都是 PHP 做的,WP)
  • 无法承载大访问量的情况(局限性)

JSP/Servlet: (JSP 本质是 Servlet)
B/S 架构:浏览器和服务器; C/S:客户端和服务器

  • sun 公司主推的 B/S 架构
  • 基于 Java 语言的(所有的大公司,或者一些开源的组件,都是用 Java 写的)
  • 可以承载三高问题带来的影响;(高并发、高可用、高性能)
  • 语法很像 ASP,很容易从 ASP → \to JSP,加强市场强度;

2.2、web服务器

服务器是一种被动的操作,用来处理用户的一些请求和给用户一些响应信息;(比如 lIS)

IIS 是微软的,用于运行 ASP,Windows 中自带的一些程序

Tomcat

在这里插入图片描述
工作中面向百度编程:

Tomcat 是 Apache 软件基金会(Apache Software Foundation) 的 jakarta 项目中的一个核心项目,最新的 Servlet 和 JSP 规范总是能在 Tomcat 中得到体现,因为 Tomcat 技术先进、性能稳定,而且免费,因而深受 java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的 Web 应用服务器。

Tomcat 服务器是一个免费的开放源代码的 Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试 JSP 程序的首选。对于一个Java初学web的人来说,它是最佳的选择

Tomcat 实际上运行JSP页面和Servlet。Tornct最新版为9.0

工作3-5年之后,可以尝试手写 Tomcat 服务器;

下载 Tomcat:

  1. 安装 or 解压
  2. 了解配置文件及目录结构
  3. 这个东西的作用

3、Tomcat

3.1、安装tomcat tomcat

官网: http://tomcat.apache.org/

我这里下载的 linux 版本
在这里插入图片描述
在这里插入图片描述

3.2、Tomcat启动和配置

1、右击“我的电脑” >> 选择“属性” >> 单击左侧栏 “高级系统设置” >>“高级”>>“环境变量”

2、在系统变量这一栏里,配置Tomcat的环境变量:
变量名:CATALINA_HOME
变量值:D:\Program Files\tomcat\apache-tomcat-9.0.93
在这里插入图片描述

找到Path变量,点击编辑:%CATALINA_HOME%\bin
在这里插入图片描述


注意还需要 JAVA 环境,%JAVA_HOME%\bin

1、闪退,说明我们环境没有配好,或者没配Java的环境(检查自己配置的变量)

2、乱码,如果界面是下面这样(乱码),这是因为我们的dos窗口和Tomcat编码类型不一致导致的

文件夹作用:
在这里插入图片描述

bin 目录下启动,关闭 Tomcat
在这里插入图片描述

访问测试: http://localhost:8080/

可能遇到的问题:

  1. Java环境变量没有配置
  2. 闪退问题:需要配置兼容性
  3. 乱码问题:配置文件中设置

可以修改 conf/logging.properties 中的 java.util.logging.ConsoleHandler.encoding = GBK 解决乱码问题

3.3、配置

在这里插入图片描述
打开这个 server.xml 文件,可以配置启动的端口号等

网页显示:
在这里插入图片描述

端口设置:
在这里插入图片描述

一个文件夹就是一个 web 应用,默认给出了 5 个 web 应用
在这里插入图片描述

现在对 ROOT 中的文件夹下的 index.jsp 进行修改。(jsp 实际上就是 js 中加入了 java 程序)
在这里插入图片描述

在这里插入图片描述

然后通过 bin 文件下的 startup.bat 启动,http://localhost:8080/ ,可以看到改动了
在这里插入图片描述

现在我们改动端口,将 conf 文件夹下的 server.xml 中的端口改为 8090,重新启动,8080 端口无法访问需要 http://localhost:8090/

在这里插入图片描述

注意 localhost 在这里改了之后是无法访问的
在这里插入图片描述
在这里插入图片描述

平时看到可以 www.baidu.com 访问。为什么呢?

在 C:\Windows\System32 下有一个 System32 文件夹,里面有我们核心配置,drivers 文件夹,里面有很多核心驱动,下面有一个 etc,是配置文件,里面有个 hosts 主机,打开 添加 127.0.0.1 www.future.com (注意改动需要在管理员权限下,先管理员权限运行记事本,然后打开)
在这里插入图片描述

在这里插入图片描述

www.future.com:8090,成功访问
在这里插入图片描述

  • tomcat 的默认端口号为:8080
  • mysql:3306
  • http:80
  • https:443

右键网页,检查,网络里面点击地址,可看到默认端口号,但是并不是所有网站都可以查看的
在这里插入图片描述

可以配置端口号

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

可以配置本地地址

  • 默认的主机名为:localhost->127.0.0.1
  • 默认网站应用存放的位置为:webapps
  <Host name="www.qinjiang.com"  appBase="webapps"
        unpackWARs="true" autoDeploy="true">

高难度面试题:

请你谈谈网站是如何进行访问的!

  1. 输入一个域名,如 www.baidu.com 或者 localhost:8080 等,回车
  2. 检查本机的 C:\Windows\System32\drivers\etc\hosts 配置文件下有没有这个域名映射;
    • 有:直接返回对应的ip地址,这个地址中,有我们需要访问的web程序,可以直接访问,比如我们上述配置的 127.0.0.1 www.future.com

    • 没有:去DNS服务器找,找到的话就返回,找不到就返回找不到;

在这里插入图片描述

4.可以配置一下环境变量(可选性)

3.4、发布一个web网站

不会就先模仿!!!

  • 将自己写的网站,放到服务器(Tomcat) 中指定的 web 应用的文件夹(webapps)下,就可以访问了

Tomcat 原始网页是寻找 ROOT 下的内容,现在 copy 一份,命名为 future
在这里插入图片描述
除了 WEB-INF 的其他文件都可以删除,WEB-INF 目录代表一个网站,WEB-INF 下面有一个 web.xml,这个是一定不能删除的。
在这里插入图片描述
打开 web.xml
在这里插入图片描述

现在开始写程序,分为静态 Web 和动态的 Web,先写一个静态的,在 future 文件夹下创建一个 index.html,并在网络上任意找了一段 html 代码如下,放入到 index.html 中,直接双击 index.html 就可以看到里面的内容

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>flex弹性布局-今日头条首页热门视频栏</title>
		<style type="text/css">
			body {
				margin: 0;
				padding: 0;
			}
			a{
				text-decoration: none;
			}
			.show-monitor {
				width: 320px;
				height: 600px;
				/* border: 2px solid red; */
				margin: 50px 0px 0px 50px;
			}

			.panel-head {
				display: flex;
				/* height: 100px; */
				/* 解除图标变形 */
				align-items: center;
			}

			.panel-head span.panel-head-title {
				/* 占满全部空间 */
				flex-grow: 1;
				font-size: 20px;
				margin-left: 10px;
			}

			.panel-head .panel-head-sx {
				font-size: 16px;
				color: red;
				margin-left: 5px;
			}

			.panel-con {
				height: 94px;
				/* background-color: antiquewhite; */
				margin-top: 20px;
				display: flex;
			}

			.panel-con .panel-con-img {
				width: 126px;
				/* 高度自动继承 */
				/* height: 94px; */
				/* background-color: aqua; */
				margin-right: 10px;
				flex-shrink: 0;
			}

			.panel-con .panel-con-img img {
				width: 100%;
				height: 100%;
				/* 裁剪图片 防止变形 */
				object-fit: cover;
			}

			.panel-con .panel-con-txt {
				/* background-color: aquamarine; */
				/* 占满剩余空间 */
				flex-grow: 1;
				display: flex;
				flex-direction: column;
				text-overflow: ellipsis;
			}
			.panel-con .panel-con-txt a{
				font-size: 16px;
				color: #222;
				/* 超过44px文字不再显示 */
				max-height: 44px;
				overflow: hidden;
				line-height: 22px;
				/* 弹性伸缩盒子模型显示 */
				display: -webkit-box;
				/* 设置或检索伸缩盒子对象的子元素的排列方式 */
				-webkit-box-orient: vertical;
				/* 限制在一个块级元素显示的文本的行数 */
				-webkit-line-clamp: 2;
				/* 文本溢出显示省略号 */
				text-overflow: ellipsis;
			}
			.panel-con .panel-con-txt span.like{
				font-size: 12px;
				background-color: #fff2f2;
				color: #f04142;
				/* 消除占满整行现象 变为内容实际所占宽度*/
				align-self: flex-start;
				padding: 3px 6px;
				border-radius: 5px;
				margin-top: 5px;
			}
			.panel-con .panel-con-txt .desc{
				font-size: 14px;
				color: #999;
				display: flex;
				justify-content: space-between;
				margin-top: 5px;
			}
		</style>
	</head>
	<body>
		<div class="show-monitor">
			<div class="panel-head">
				<img src="images/icon-play.png" alt="" width="22">
				<span class="panel-head-title">热门视频</span>
				<img src="images/icon-sx2.png" alt="" width="16">
				<span class="panel-head-sx">换一换</span>
			</div>
			<div class="panel-con">
				<div class="panel-con-img">
					<a href=""><img src="images/toutiao-01.jpeg" alt=""></a>
				</div>
				<div class="panel-con-txt">
					<a href="">司马南:中国与俄罗斯的战线</a>
					<span class="like">1万评论</span>
					<div class="desc">
						<span>148万次观看</span>
						<span>司马南频道</span>
					</div>
				</div>
			</div>
			<div class="panel-con">
				<div class="panel-con-img">
					<a href=""><img src="images/toutiao-02.jpeg" alt=""></a>
				</div>
				<div class="panel-con-txt">
					<a href="">无论做什么鱼:最忌放盐和料酒研制,大厨教你绝招.</a>
					<span class="like">1万评论</span>
					<div class="desc">
						<span>148万次观看</span>
						<span>司马南频道</span>
					</div>
				</div>
			</div>
			<div class="panel-con">
				<div class="panel-con-img">
					<a href=""><img src="images/toutiao-03.jpeg" alt=""></a>
				</div>
				<div class="panel-con-txt">
					<a href="">司马南:中国与俄罗斯的战线</a>
					<span class="like">1万评论</span>
					<div class="desc">
						<span>148万次观看</span>
						<span>司马南频道</span>
					</div>
				</div>
			</div>
			<div class="panel-con">
				<div class="panel-con-img">
					<a href=""><img src="images/toutiao-04.jpeg" alt=""></a>
				</div>
				<div class="panel-con-txt">
					<a href="">司马南:中国与俄罗斯的战线</a>
					<span class="like">1万评论</span>
					<div class="desc">
						<span>148万次观看</span>
						<span>司马南频道</span>
					</div>
				</div>
			</div>
		</div>
	</body>
</html>

重新启动 Tomcat,它会自动访问 ROOT 下面的内容,进一步输入 localhost:8090/future/,就会访问 future 文件夹下的资源,实际会自动添加 localhost:8090/future/index.html,静态 web,不会动的
在这里插入图片描述

同理,还可以通过 localhost:8090/docs/ 访问 docs 文件夹下的内容,其他文件下的内容同理
在这里插入图片描述

重点是 examples 文件夹下有很多例子,访问 localhost:8090/examples/
在这里插入图片描述

比如进入 Servlet Examples,很多例子可以执行,可以通过 Source 查看源码,自学就可以通过这些例子来查看源码
在这里插入图片描述

网站应该有的结构

--webapps :Tomcat服务器的web目录
	-ROOT
	-kuangstudy :网站的目录名
		- WEB-INF
			-classes : java程序
			-lib:web 应用所依赖的jar包
			-web.xml :网站配置文件
		- index.html 默认的首页
		- static 
            -css
            	-style.css
            -js
            -img
         -.....

HTTP 协议: 面试

4.HTTP

4.2 什么是 HTTP?

HTTP(hypertext transport protocol) 超文本传输协议。

  • 文本 :HTML,字符串…
  • 超文本:图片,音乐,视频,定位,地图…
  • 80 端口

HTTPS(Hyper Text Transfer Protocol over SecureSocket Layer):是以安全为目标的 HTTP 通道,在 HTTP 的基础上通过传输加密和身份认证保证了传输过程的安全性。HTTPS 在HTTP 的基础下加入SSL 层,HTTPS 的安全基础是 SSL。

  • 443 端口

4.2 两个时代

HTTP 1.0:

  • HTTP/1.0:客户端与 Web 服务器连接后,只能获得一个 Web 资源,然后就断开连接,加入某个页面有多个图片资源需要加载,那么需要连接多次,影响服务器和客户端的性能。

HTTP 2.0:

  • HTTP/1.1:客户端可以与web服务器连接后,可以获得多个web资源。

4.3 HTTP 请求

客户端 -> 发送请求(Request)->服务器

百度:

Request URL:https://www.baidu.com/   请求地址
Request Method:GET    get方法/post方法
Status Code:200 OK    状态码:200
Remote(远程) Address:14.215.177.39:443
Accept:text/html  
Accept-Encoding:gzip, deflate, br
Accept-Language:zh-CN,zh;q=0.9    语言
Cache-Control:max-age=0
Connection:keep-alive

1、请求行

请求行中的请求方式:GET
请求方式:Get,Post,HEAD,DELETE,PUT,TRACT…

  • get:请求能够携带的参数比较少,大小有限制,会在浏览器的URL地址栏显示数据内容,不安全,但高效
  • post:请求能够携带的参数没有限制,大小没有限制,不会在浏览器的URL地址栏显示数据内容,安全,但不高效。

2、消息头

Accept:告诉浏览器,它所支持的数据类型
Accept-Encoding:支持哪种编码格式  GBK   UTF-8   GB2312  ISO8859-1
Accept-Language:告诉浏览器,它的语言环境
Cache-Control:缓存控制
Connection:告诉浏览器,请求完成是断开还是保持连接
HOST:主机..../.

4.4 HTTP 响应

  • 服务器 -> 响应(response) ->客户端

百度:

Cache-Control:private    缓存控制
Connection:Keep-Alive    连接
Content-Encoding:gzip    编码
Content-Type:text/html   类型

1、响应体

Accept:告诉浏览器,它所支持的数据类型
Accept-Encoding:支持哪种编码格式  GBK   UTF-8   GB2312  ISO8859-1
Accept-Language:告诉浏览器,它的语言环境
Cache-Control:缓存控制
Connection:告诉浏览器,请求完成是断开还是保持连接
HOST:主机..../.
Refresh:告诉客户端,多久刷新一次;
Location:让网页重新定位;

2、响应状态码

200:请求响应成功 200

3xx:请求重定向

  • 重定向:你重新到我给你新位置去;

4xx:找不到资源 404
5xx:服务器代码错误 500 502:网关错误

常见面试题:
当你的浏览器中地址栏输入地址并回车的一瞬间到页面能够展示回来,经历了什么?

5. Maven

  • 在 JavaWeb 开发中,需要使用大量的 jar 包,我们手动去导入;
  • 如何能够让一个东西自动帮我导入和配置这个jar包。

由此,Maven诞生了!

5.5 标记文件夹功能

通过选中文件夹,将其标记为 maven 对应的文件夹
在这里插入图片描述

在这里插入图片描述


还有一种标记方式如下:
在这里插入图片描述

可以通过这里点击文件夹,然后点击 Mark as 的一个标记来标记此文件夹
在这里插入图片描述

5.6 在 IDEA 中配置 Tomcat

先创建一个maven项目,选择 webapp
在这里插入图片描述

结构如下:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

解决警告问题

必须要的配置:为什么会有这个问题:因为我们访问一个网站,需要指定一个文件夹名字(之前是默认 webapps 这个文件),IDEA 配置 tomcat 时没有这个默认文件,所以我们要手动配置

在这里插入图片描述
选择 war,就配置好啦
在这里插入图片描述

在这里点击启动
在这里插入图片描述

成功访问
在这里插入图片描述

可能存在 Maven 默认 Web 项目中的 web.xml 版本问题
在这里插入图片描述

在这里插入图片描述

替换为 webapp4.0 版本和 Tomcat 一致

<?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"
         metadata-complete="true">



</web-app>

5.7 部分其他问题

Maven 由于它的约定大于配置,之后可以能遇到我们写的配置文件,无法被导出或者生效的问题,解决方案:

在 pom.xml 中配置,比如默认情况我们在资源文件 resources 下写的 properties 文件才会被打包,在 java 源代码文件下撰写的 properties 文件最终是无法被导出的,为了解决这个问题添加配置信息如下半部分

<!--在build中配置resources,来防止我们资源导出失败的问题-->
<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

6. Servlet 简介

Servlet 就是 Sun 公司开发动态 Web 的一门技术

Sun 在这些 API (Application Programming Interface,应用程序接口)中提供一个接口叫做:Servlet,如果你想开发一个Servlet程序,只需要完成两个小步骤:

  • 编写一个类,实现Servlet接口。
  • 把开发好的Java类部署到web服务器中。

把实现了 Servlet 接口的 Java 程序叫做,Servlet

6.2 HelloServlet

Serlvet 接口 Sun 公司有两个默认的实现类:HttpServlet,GenericServlet

  1. 构建一个普通的 Maven 项目,删掉里面的 src 目录,以后我们的学习就在这个项目里面建立 Moudel;这个空的工程就是Maven主工程(建一个WebApp Maven项目,勾选模板),因为可以在项目内构建多个子工程,每个子工程都有自己的 pom.xml;
    在这里插入图片描述

尽可能的把所有依赖都导入到主工程的 pom.xml 中,在 maven 仓库 中,获取 servlet 的依赖,加入到主工程中。

<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
    <scope>provided</scope>
</dependency>

同时把 JSP 的依赖添加到主工程中

<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>javax.servlet.jsp-api</artifactId>
    <version>2.3.3</version>
    <scope>provided</scope>
</dependency>

通常只有版本信息会变 version 字段
在这里插入图片描述

构建一个 servlet 项目,新建 Module
在这里插入图片描述

由于 servlet 是一个 web 项目,选中模版中的 org.apache.maven.archetypes:maven-archetype-webapp
在这里插入图片描述
在这里插入图片描述

  1. 关于Maven父子工程的理解:

父项目的 pom.xml 中会有:

<modules>
  <module>servlet-01</module>
</modules>

如果多个子项目,父项目的 pom.xml 中会有

<modules>
  <module>servlet-01</module>
  <module>subproject-01</module>
  <module>subproject-02</module>
  <module>subproject-03</module>
  ....
</modules>

子项目中会有:

<parent>
    <artifactId>Servlet</artifactId>
    <groupId>org.example</groupId>
    <version>1.0-SNAPSHOT</version>
</parent>

父项目中的 jar 包子项目可以直接使用,但是子项目中的,父项目不可以使用

son extends father
  1. Maven 环境优化
  • 修改 web.xml 为最新的
  • 将 maven 的结构搭建完整

产生 servlet-01 子项目后,第一步,将 src/main/webapp/WEB-INF/web.xml 中的内容换为最新的(初始版太老了)

<?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"
         metadata-complete="true">



</web-app>

然后再 src/main 下,新建 java、resources ,并标记为 源代码文件和资源文件
在这里插入图片描述

  1. 编写一个Servlet程序
  • 编写一个普通类
  • 实现 Servlet 接口,这里我们直接继承 HttpServlet

在这里插入图片描述

建包
在这里插入图片描述

创建类 HelloServlet
在这里插入图片描述

然后让类继承 HttpServlet 接口,子工程会自动导包父工程
在这里插入图片描述

如果无法正常自动导包父工程中的 jar 包,检查父工程 pom.xml 如下:(外层不要使用
dependencyManagement,注释掉 scope)
在这里插入图片描述

重写 get,post 方法

在这里插入图片描述

S1:编写一个普通类
S2:实现 Servlet 接口,这里我们直接继承 HttpServlet

由于get或者post只是请求实现的不同的方式,可以相互调用,业务逻辑都一样;

public class HelloServlet extends HttpServlet {

    //由于get或者post只是请求实现的不同的方式,可以相互调用,业务逻辑都一样;
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //ServletOutputStream outputStream = resp.getOutputStream();
        PrintWriter writer = resp.getWriter(); //响应流
        writer.print("Hello,Serlvet");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
  1. 编写 Servlet 的映射

为什么需要映射:我们写的是 Java 程序,但是要通过浏览器访问,而浏览器需要连接 Web 服务器,所以我们需要在 Web 服务中注册我们写的 Servlet,还需给他一个浏览器能够访问的路径;

<!--注册Servlet-->
<servlet>
    <servlet-name>hello</servlet-name>
    <servlet-class>com.uestc.servlet.HelloServlet</servlet-class>
</servlet>
<!--Servlet的请求路径-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>

在这里插入图片描述

  1. 配置 Tomcat
    注意: 配置项目发布的路径就可以了

在这里插入图片描述
在这里插入图片描述

配置好后,解决警告,点击 Fix
在这里插入图片描述

会进入到 Deployment,选择项目 servlet-01:war
在这里插入图片描述

或者点击 + 号,选择则 artifacts,再设置,其中 Application context 设置为 /s1,表示请求
在这里插入图片描述

  1. 启动测试,OK!

在这里插入图片描述

项目下面会产生一个 target 文件夹,里面就是我们项目的信息
在这里插入图片描述

成功访问,我们设置的 8090 端口,s1 下进行访问,成功!(通常是 8080 端口)
在这里插入图片描述

网页上查找 hello 的请求路径
在这里插入图片描述

会在 Servlet 的请求路径中查看 url-pattern,找到 hello,获取到 name 为 hello,在 servlet 中找到 hello,获取到对应的类,如下:
在这里插入图片描述

6.3、Servlet原理

Servlet是由Web服务器调用,浏览器会向 Web 容器发送 http 请求(request 请求),如果是首次访问,会将我们编写的 java 类编译成 class 类去运行,如果项目比较庞大,第一次会比较慢,后面就会很快。

在这里插入图片描述

web 容器会产生两个对象,请求(request)和响应(response),servlet 里面有个 service 方法,这个方法可以处理请求和响应,因此 请求(request)和响应(response)对象会去调用这个 service 方法。 Request 从 service 方法中拿到请求,并把请求之后的响应交给 Response
在这里插入图片描述

获取响应信息的过程如下:
在这里插入图片描述

获取到响应信息后就可以返回给客户端了,整个流程如下
在这里插入图片描述

6.4 Mapping 问题

  1. Mapping 是请求映射的一个路径,一个 Servlet 指定一个映射路径的情况:
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>
  1. 一个 Servlet 指定多个映射路径的情况
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello2</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello3</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello4</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello5</url-pattern>
</servlet-mapping>
  1. 一个Servlet指定通用映射路径,使用 /*,这个通配符
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/hello/*</url-pattern>
</servlet-mapping>
  1. 默认请求路径(就是什么都不写,会走如下请求)
<!--默认请求路径-->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>
  1. 指定一些后缀或者前缀等等….
<!--可以自定义后缀实现请求映射
    注意点,*前面不能加项目映射的路径
    hello/sajdlkajda.qinjiang
    -->
<servlet-mapping>
    <servlet-name>hello</servlet-name>
    <url-pattern>*.qinjiang</url-pattern> <!-- 不能写成 /*.qinjiang --> 
</servlet-mapping>

1. 使用 /* (通用路径匹配)

<servlet-mapping>  
    <servlet-name>hello</servlet-name>  
    <url-pattern>/hello/*</url-pattern>  
</servlet-mapping>  
  • 匹配路径:匹配所有以 /hello/ 开头的路径,例如 /hello/abc/hello/xyz/def
  • 用途:这种方式通常用于匹配某个特定前缀后的所有路径。可以让 servlet 处理以某个特定路径为前缀的所有请求。
  • 说明/* 会匹配 /hello/ 后面的所有路径和子路径。例如,/hello/test/hello/abc/xyz 都会被 hello servlet 处理。

2. 默认请求路径 /*

<!--默认请求路径-->  
<servlet-mapping>  
    <servlet-name>hello</servlet-name>  
    <url-pattern>/*</url-pattern>  
</servlet-mapping>  
  • 匹配路径:这种配置会匹配所有的请求路径,包括根路径和所有的子路径,例如 /abc/hello/xyz/abc 等。
  • 用途:适用于在没有明确指定路径的情况下,处理所有请求。非常常见于默认 servlet 或通用处理请求的场景。需要谨慎使用,因为它会拦截所有的请求,包括静态资源(如图片、CSS、JS 等)。
  • 说明/* 匹配所有请求,通常作为全局的默认映射。

3. 使用后缀(自定义后缀)

<!--可以自定义后缀实现请求映射-->  
<servlet-mapping>  
    <servlet-name>hello</servlet-name>  
    <url-pattern>*.qinjiang</url-pattern>  
</servlet-mapping>  
  • 匹配路径:只会匹配以 .qinjiang 为后缀的路径。例如,/hello/something.qinjiang 会被 hello servlet 处理。
  • 用途:用于将特定的 URL 后缀映射到指定的 servlet。例如,某些特殊类型的文件请求可以由某个 servlet 处理,而其他请求则交给其他 servlet 或静态资源处理。
  • 说明:这种方式比较特殊,通常用于基于文件后缀的请求路由。**注意:**不能在前面加上 /,因为 url-pattern 是通过匹配路径来识别请求的,* 只能匹配文件名的后缀。

总结

  • /*(例如 /hello/*)用于匹配以某个特定路径开头的所有请求(支持多层级路径)。
  • /* 用于匹配所有请求路径(全局匹配)。
  • *.qinjiang 用于匹配特定后缀的请求,通常用于指定文件类型的请求处理。

选择使用哪种方式,取决于你的需求:

  • 如果你需要针对特定路径做处理,使用 /hello/*
  • 如果你需要拦截所有请求,使用 /*
  • 如果你需要根据文件后缀进行处理,使用 *.qinjiang
  1. 优先级问题
    指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求;
<!--404-->
<servlet>
    <servlet-name>error</servlet-name>
    <servlet-class>com.kuang.servlet.ErrorServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>error</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

6.5 ServletContext对象

Web 应用程序的上下文(Web Application Context)是指 Web 应用程序在运行时的环境和状态信息的集合。它提供了一个在整个应用程序范围内共享的区域,包含了应用的配置、资源、参数和一些状态信息。在 Servlet 容器中,ServletContext 就是表示 Web 应用程序上下文的对象。它提供了跨 Servlet 共享数据、访问应用参数、获取资源、日志记录等功能。

主要功能:

  1. 共享资源:通过 setAttribute()getAttribute() 方法,多个 Servlet 可以共享数据。
  2. 访问应用参数:通过 getInitParameter() 获取 web.xml 中的初始化参数。
  3. 资源访问:通过 getResource()getResourceAsStream() 获取应用中的静态资源。
  4. 日志记录:使用 log() 方法记录应用日志。
  5. 获取服务器信息:使用 getServerInfo() 获取容器的基本信息。

举例:

在 Servlet 容器中,当应用启动时,容器为每个 Web 应用程序创建一个 ServletContext 对象,该对象管理该应用程序的配置、资源、生命周期等信息。

总结:

Web 应用程序的上下文是一个虚拟环境,包含了 Web 应用的配置、资源、共享数据和生命周期信息。它使得不同的组件(如 Servlet、Filter 等)能够在同一个 Web 应用内共享信息和资源。ServletContext 用于跨 Servlet 共享资源、访问应用级参数、获取资源及日志记录,是 Web 应用的重要上下文对象。

先在父工程下新建一个 servlet-02 项目
在这里插入图片描述

父工程的 pom.xml 多了一个模块
在这里插入图片描述

更改 servlet-02 中的 src/main/webapp/WEB-INF/web.xml,如下

<?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"
         metadata-complete="true">

</web-app>

添加 源码目录(java)和资源目录(resources),进行标记
在这里插入图片描述

如果:Maven pom.xml 文件变灰且中间有一条横线的处理办法:
是因为 pom.xml 文件被设置在maven忽略文件清单中
解决方法: file -->setting–>maven–>Ignored Files 将清单中对应项目的pom.xml文件取消选中即可;

创建 HelloServlet

public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Hello"); // 向后台输入也能测试 servlet
    }
}

在这里插入图片描述

然后在 web.xml 中注册 Servlet

    <!--注册Servlet-->
    <servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class>com.uestc.servlet.HelloServlet</servlet-class>
    </servlet>
    <!--Servlet的请求路径-->
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

Tomcat 配置
在这里插入图片描述

多了一个 servlet-02 war 包,添加进入,并去掉之前的 servlet-01:war,需要保持这里面只有一个 war 包,因为添加两个打包会很慢
在这里插入图片描述
在这里插入图片描述

然后在网页上走 hello 这个请求 http://localhost:8090/s2/hello,页面没有任何东西,后台输出了 hello(因为我们上面代码的 response 没有响应到页面,只是让后台输出了 hello)
在这里插入图片描述

查看 HttpServlet 类中的方法
在这里插入图片描述

下面我们重点来看 ServletContext:web 容器在启动的时候,他会为每个 web 程序都创建一个对应的ServletContext 对象,它代表了当前的 web 应用;

思考一个问题:不同的 Servlet 之间能否互相传送数据。(能,保存在 ServletContext 中,其他 Servlet 直接读取里面的东西即可)
在这里插入图片描述

更改 Servelt-02 的 HelloServlet 如下,将数据保存到 ServletContext

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        
        //this.getInitParameter()   初始化参数
        //this.getServletConfig()   Servlet配置
        //this.getServletContext()  Servlet上下文
        ServletContext context = this.getServletContext();

        String username = "青年有志"; // 数据,这里使用中文,可以导致乱码
        context.setAttribute("username",username); //将一个数据保存在了ServletContext中,名字为:username 。值 username
    }
}

在 Servelt-02 中创建一个 GetServlet
在这里插入图片描述

public class GetServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        String username = (String) context.getAttribute("username");

        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8"); // 正常显示中文
        resp.getWriter().print("名字"+username);

    }

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

在 Servelt-02 下的 web.xml 中进行配置
在这里插入图片描述
配置名字为 getc(自定义)

    <servlet>
        <servlet-name>getc</servlet-name>
        <servlet-class>com.uestc.servlet.GetServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>getc</servlet-name>
        <url-pattern>/getc</url-pattern>
    </servlet-mapping>

重启 Tomcat,我们先请求 getc,为 null
在这里插入图片描述

那么我们先请求 hello,什么都没有,很正常,代码只实现了向 ServletContext 中写入数据
在这里插入图片描述

再执行请求 getc,成功获取!
在这里插入图片描述

6.5.1 共享数据

总结上述过程:在一个 servlet 中保存的数据,可以在另一个 servlet 中拿到;

需要一个放置数据的类

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        
        //this.getInitParameter()   初始化参数
        //this.getServletConfig()   Servlet配置
        //this.getServletContext()  Servlet上下文
        ServletContext context = this.getServletContext();

        String username = "秦疆"; //数据
        context.setAttribute("username",username); //将一个数据保存在了ServletContext中,名字为:username 。值 username
    }
}

读取数据的类

public class GetServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();
        String username = (String) context.getAttribute("username");

        resp.setContentType("text/html");
        resp.setCharacterEncoding("utf-8");
        resp.getWriter().print("名字"+username);

    }

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

最后注册 Servlet

    <servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class>com.kuang.servlet.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>


    <servlet>
        <servlet-name>getc</servlet-name>
        <servlet-class>com.kuang.servlet.GetServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>getc</servlet-name>
        <url-pattern>/getc</url-pattern>
    </servlet-mapping>

6.5.2 获取初始化参数 <context-param>

元素在 web.xml 中的作用是为 Web 应用程序提供全局的上下文参数。这些参数可以在整个应用程序中被访问,并且可以用于配置、初始化参数等。它们通常用于配置在整个 Web 应用中都需要访问的一些常量值,例如数据库连接、API 密钥、文件路径等。

使用 <context-param> 的基本语法

<context-param>
    <param-name>参数名称</param-name>
    <param-value>参数值</param-value>
</context-param>
  • <param-name>:这是参数的名称,定义了这个上下文参数的标识符。
  • <param-value>:这是参数的值,通常是一个字符串,可以是任意的配置信息。

典型的使用场景

  1. 数据库连接配置
    在 Web 应用程序中,你可能需要配置数据库连接的 URL、用户名、密码等信息,可以通过 <context-param> 来提供这些信息。

    <context-param>
        <param-name>db.url</param-name>
        <param-value>jdbc:mysql://localhost:3306/mydatabase</param-value>
    </context-param>
    <context-param>
        <param-name>db.username</param-name>
        <param-value>root</param-value>
    </context-param>
    <context-param>
        <param-name>db.password</param-name>
        <param-value>password123</param-value>
    </context-param>
    
  2. 文件路径或配置路径
    对于一些需要特定文件路径或者配置文件路径的情况,也可以通过 <context-param> 来传递这些路径信息。

    <context-param>
        <param-name>config.file.path</param-name>
        <param-value>/path/to/config.xml</param-value>
    </context-param>
    
  3. 外部服务或 API 配置
    如果应用需要访问外部 API 或服务,可以通过 <context-param> 存储 API 密钥、URL 等信息。

    <context-param>
        <param-name>api.key</param-name>
        <param-value>12345abcdef</param-value>
    </context-param>
    

访问 <context-param> 参数

一旦在 web.xml 中定义了 <context-param>,你就可以在应用程序的 Java 代码中访问这些参数。访问方式通常是通过 ServletContext 对象,下面是几种常见的访问方法:

1. 使用 ServletContext 访问参数
// 获取 ServletContext 对象
ServletContext context = getServletContext();

// 获取参数值
String dbUrl = context.getInitParameter("db.url");
String dbUsername = context.getInitParameter("db.username");
String dbPassword = context.getInitParameter("db.password");
2. 在 ServletFilter 中使用

ServletFilter 中也可以轻松访问这些参数。比如:

@WebServlet("/example")
public class ExampleServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取 context-param 参数
        ServletContext context = getServletContext();
        String dbUrl = context.getInitParameter("db.url");

        // 使用 dbUrl 做其他操作
        response.getWriter().write("Database URL: " + dbUrl);
    }
}
3. 在 Listener 中使用

如果你需要在应用启动时进行初始化操作,也可以在 ServletContextListener 中访问这些参数:

public class AppContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext context = sce.getServletContext();
        String dbUrl = context.getInitParameter("db.url");
        // 在此处使用 dbUrl 进行初始化数据库连接等操作
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        // 处理清理操作
    }
}

相关注意事项

  • 参数名称唯一性<param-name> 应该具有唯一性,以避免多个参数具有相同的名称,导致混淆。

  • 参数值类型<param-value> 中存储的通常是字符串类型的数据,但也可以通过解析或转换来处理更复杂的配置。例如,你可以将 JSON 配置字符串存储在 <param-value> 中,启动时再将其解析为对象。

  • 访问范围:这些参数是全局的,因此可以在整个 Web 应用程序中使用,但需要注意,如果多个模块需要不同的值,最好分开使用不同的参数名称来避免冲突。

  • 配置加载顺序:Web 应用程序启动时,web.xml 中的所有 <context-param> 会被读取,并可供应用中的所有组件使用。所以这些参数的加载顺序是先于 Servlet、Filter 等组件的初始化。

示例:web.xml 配置

<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                             http://java.sun.com/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
  
    <context-param>
        <param-name>db.url</param-name>
        <param-value>jdbc:mysql://localhost:3306/mydatabase</param-value>
    </context-param>
    
    <context-param>
        <param-name>db.username</param-name>
        <param-value>root</param-value>
    </context-param>
    
    <context-param>
        <param-name>db.password</param-name>
        <param-value>password123</param-value>
    </context-param>
    
    <servlet>
        <servlet-name>exampleServlet</servlet-name>
        <servlet-class>com.example.ExampleServlet</servlet-class>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>exampleServlet</servlet-name>
        <url-pattern>/example</url-pattern>
    </servlet-mapping>
    
</web-app>

在上面的示例中,<context-param> 用于配置数据库的连接信息,这些信息可以在 Web 应用程序中的各个 Servlet 或其他组件中通过 ServletContext 来访问。

<!--配置一些 web 应用初始化参数,比如配置 jdbc 连接-->
<context-param>
    <param-name>url</param-name>
    <param-value>jdbc:mysql://localhost:3306/mybatis</param-value>
</context-param>

下面在 Servlet-02 中创建类 ServletDemo03,并打印配置的 url

ServletContext context = this.getServletContext();

String url = context.getInitParameter("url");

resp.getWriter().print(url);

在这里插入图片描述

进行 Servlet 注册

<servlet>
<servlet-name>gp</servlet-name>
    <servlet-class>com.uestc.servlet.ServletDemo03</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>gp</servlet-name>
    <url-pattern>/gp</url-pattern>
</servlet-mapping>

在这里插入图片描述

然后重启 Tomcat,然后请求 gp,localhost:8090/s2/gp,就拿到了上述我们配置的 url
在这里插入图片描述

6.5.3、请求转发

在 servlet-02 下面新建一个 ServletDemo04

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    ServletContext context = this.getServletContext();
    System.out.println("进入了ServletDemo04"); // 查看是否成功进入
    //RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp"); //转发的请求路径,当前页面下的 gp
    //requestDispatcher.forward(req,resp); //调用forward实现请求转发;
    context.getRequestDispatcher("/gp").forward(req,resp); // 上面两行合并为一行
}

注册 servlet

    <servlet>
        <servlet-name>sd4</servlet-name>
        <servlet-class>com.uestc.servlet.ServletDemo04</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>sd4</servlet-name>
        <url-pattern>/sd4</url-pattern>
    </servlet-mapping>

重启 Tomcat,然后请求 sd4,获取到的是请求 gp 时的内容,这就是请求转发的效果,但是路径是不会改变的,还是 localhost:8090/s2/sd4,后面讲的重定向才会发生改变
在这里插入图片描述

A 想要拿到 C 的资源,但是无法直接调用 C,只能找到 B,可以先 B 找 C 拿,C 再给 B,B 再给 A,这就是请求转发,这样 A 的路径是没有变化的,只有转发的概念,如下图上部分

什么是重定向?A 告诉 B ,要拿一个资源,但是资源只有 C 有,B 就告诉 A ,去 C 哪里拿,A 又去找 C,到时候会发生跳转,如下图下半部分

在这里插入图片描述

在 Java Web 开发中,请求转发(Request Forwarding)是一种由服务器端处理的请求流转机制,它允许将客户端的请求从一个 Servlet 转发到另一个 Servlet、JSP 页面或其他资源,而无需客户端知道这一过程。

context.getRequestDispatcher("/gp").forward(req, resp); 语句是实现请求转发的一种方式

1. 什么是请求转发?

请求转发是服务器端的一种处理方式,其中服务器将客户端的请求(HttpServletRequest)转发到另一个资源(如另一个 Servlet、JSP 页面、HTML 页面等)。转发后,客户端并不知道请求被转发到哪里,它只知道发出了请求,而最终的响应是由转发后的资源生成并返回的。

2. RequestDispatcher 接口

RequestDispatcher 是 Java Servlet API 中的一个接口,它提供了两种主要的方法来转发请求或包括资源的内容:

  • forward(ServletRequest request, ServletResponse response):将请求转发到另一个资源(Servlet、JSP 页面等),请求会继续在服务器端处理,而客户端并不会知道这个过程。转发后,控制权交给目标资源,原请求的 requestresponse 会被传递给目标资源。
  • include(ServletRequest request, ServletResponse response):将请求的响应内容包含在另一个资源的响应中。通常是将另一个 Servlet 或 JSP 页面的内容插入到当前页面中。

3. context.getRequestDispatcher("/gp").forward(req, resp) 解释

context.getRequestDispatcher("/gp").forward(req, resp);  
  • context: 这里的 contextServletContext 对象,它表示应用的上下文。可以通过 getServletContext() 方法获取它。
  • getRequestDispatcher("/gp"): getRequestDispatcher 方法返回一个 RequestDispatcher 对象,表示可以将请求转发到指定路径的资源。这里的 "/gp" 是转发的目标路径,它可以是:
    • 相对路径/gp 是从 Web 应用的根目录开始的相对路径,也可以是 /contextPath/gp。如果路径是 /gp,那么它会映射到 Web 应用根目录下的 gp 资源(例如 gp 对应的 Servlet 或 JSP)。
    • 绝对路径getRequestDispatcher("/somePath") 表示从 Web 应用根目录开始的路径。注意这里的路径是相对于 Web 应用的根路径的,而不是相对于当前请求的路径。
  • forward(req, resp):这将把当前请求转发到 /gp 路径指定的资源,并将原始的请求对象(req)和响应对象(resp)传递给目标资源。转发后,控制权交给 /gp 对应的资源进行处理。

4. 请求转发的特点

  • 服务器端操作:请求转发完全由服务器处理,客户端(浏览器)并不知道请求已经被转发。
  • 共享请求和响应对象:在转发过程中,原始的 requestresponse 对象会被传递给目标资源。因此,转发后的资源可以访问到原始请求中的所有参数、属性等。
  • URL 不变:请求转发后,客户端浏览器的 URL 不会发生变化,仍然是最初请求的 URL。这是请求转发与重定向的一个主要区别。

5. 请求转发的应用场景

请求转发通常用于以下几种情况:

  1. 请求链处理
  • 在 Web 应用中,一个请求可能需要经过多个 Servlet 或资源的处理。例如,一个请求在一个 Servlet 中进行验证或设置了数据,然后转发到另一个 Servlet 或 JSP 页面处理结果并展示给用户。
  • 示例:用户登录后,先由一个 Servlet 进行身份验证,然后将验证结果转发到一个显示用户信息的页面。
  1. JSP 页面之间的跳转
  • 一个 Servlet 或一个 JSP 页面可以通过请求转发将请求传递给另一个 JSP 页面,生成最终的响应。
  • 示例:一个 Servlet 处理了表单提交的逻辑后,将请求转发到一个 JSP 页面显示结果。
  1. 分布式 Web 应用中的请求处理
  • 在某些分布式 Web 应用中,一个 Servlet 可能会将请求转发到另一台服务器上的资源进行处理。

6. 转发与重定向的区别

请求转发和重定向都是 Web 开发中常用的请求处理方式,它们有以下不同之处:

特点请求转发 (forward)重定向 (sendRedirect)
控制权由服务器控制,客户端不知道转发过程由客户端控制,浏览器会重新发起请求
URL 变化不会变化,浏览器 URL 不变会变化,浏览器 URL 会更新为目标 URL
请求传递请求对象和响应对象会传递给目标资源新的请求会发往服务器,原请求信息丢失
请求次数只有一次请求会发起两次请求:一次到当前资源,另一次到目标资源
适用场景服务器端的资源跳转,内部请求处理链客户端跳转,或者跨应用的请求

7. 实际示例

假设我们有以下的 Web 应用结构:

/webapp  
├── /index.jsp  
├── /loginServlet  
└── /welcome.jsp  

loginServlet 负责处理登录逻辑,登录成功后将请求转发到 welcome.jsp 页面:

LoginServlet.java
@WebServlet("/loginServlet")  
public class LoginServlet extends HttpServlet {  
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {  
        String username = request.getParameter("username");  
        String password = request.getParameter("password");  
  
        // 假设验证成功  
        if ("user".equals(username) && "pass".equals(password)) {  
            // 登录成功,转发到欢迎页面  
            request.getRequestDispatcher("/welcome.jsp").forward(request, response);  
        } else {  
            // 登录失败,转发回登录页面  
            request.getRequestDispatcher("/index.jsp").forward(request, response);  
        }  
    }  
}  
welcome.jsp
<html>  
<body>  
    <h1>Welcome, you have logged in successfully!</h1>  
</body>  
</html>  

8. 总结

  • 请求转发是服务器端的操作,客户端无法察觉,适用于在服务器端处理多个资源之间的请求。
  • 通过 RequestDispatcherforward() 方法实现请求的转发。
  • 请求转发不会改变浏览器 URL,且请求和响应对象保持一致,适合用于多个资源的内部处理链。

6.5.4、读取资源文件 properties 文件内容

在 Web 应用中,通常资源文件(例如 db.properties)会被放置在类路径中,例如 WEB-INF/classes 目录下。在这种情况下,您不需要硬编码绝对路径,而是依赖于类路径来动态访问文件。下面我将详细说明如何在 Web 应用中通过 HTTP 请求加载 db.properties 文件。

1. 类路径概念

在 Java Web 应用中,所有的 Java 类文件和资源文件(例如 .properties 文件、.xml 文件等)最终都会打包到应用的类路径中。通常:

  • 编译后的 .class 文件放在 WEB-INF/classes 目录下。
  • 资源文件(如配置文件)也应放在 WEB-INF/classesWEB-INF 下的其他适当目录。

类路径是 Java 应用程序查找类文件和资源文件的地方。在 Web 应用中,类路径通常是 Web 应用的根目录下的 WEB-INF/classes 目录,所有的资源文件都会被部署到这个路径中。

2. Web 应用中的资源访问方式

在 Web 应用中,使用 ServletContext 来访问 WEB-INF 下的资源文件。通过 getResourceAsStream() 方法,可以从类路径中读取资源文件,而不需要关心文件的绝对路径。

3. 加载 db.properties 配置文件的步骤

假设 db.properties 文件存放在 WEB-INF/classes/com/yourapp/config/db.properties 中,您可以通过如下步骤从请求中加载该文件。

代码实现

import javax.servlet.ServletException;  
import javax.servlet.http.*;  
import java.io.IOException;  
import java.io.InputStream;  
import java.util.Properties;  
 
public class ServletDemo extends HttpServlet {  
 
    @Override  
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {  
        
        // 使用 getServletContext() 获取资源输入流,路径为 /WEB-INF/classes/com/yourapp/config/db.properties  
        InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/com/yourapp/config/db.properties");  
 
        // 如果文件没有找到,返回错误消息  
        if (is == null) {  
            resp.getWriter().print("db.properties not found!");  
            return;  
        }  
 
        // 创建 Properties 对象,用于读取配置文件内容  
        Properties prop = new Properties();  
        prop.load(is);  
 
        // 获取属性值(例如数据库连接信息)  
        String dbUrl = prop.getProperty("db.url");  
        String dbUser = prop.getProperty("db.username");  
        String dbPassword = prop.getProperty("db.password");  
 
        // 将读取到的配置信息输出到浏览器  
        resp.getWriter().print("Database URL: " + dbUrl + "<br>");  
        resp.getWriter().print("Username: " + dbUser + "<br>");  
        resp.getWriter().print("Password: " + dbPassword + "<br>");  
    }  
 
    @Override  
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {  
        // 处理 POST 请求,调用 doGet 进行处理  
        doGet(req, resp);  
    }  
}  

解释

  1. 加载配置文件

    • getServletContext().getResourceAsStream("/WEB-INF/classes/com/yourapp/config/db.properties")
      • 这行代码通过 ServletContext 来加载 db.properties 配置文件。路径 /WEB-INF/classes/com/yourapp/config/db.properties 表示相对于 Web 应用的根路径。
      • getResourceAsStream() 会返回一个输入流 InputStream,您可以用它来读取文件内容。
  2. 读取文件内容

    • 使用 Properties 类来读取 .properties 文件的键值对。prop.load(is) 会从 InputStream 加载配置项。
    • 通过 prop.getProperty("db.url")prop.getProperty("db.username")prop.getProperty("db.password") 获取数据库连接的相关配置。
  3. 返回响应

    • 将数据库的配置信息输出到网页上,使用 resp.getWriter().print() 将信息打印到浏览器。

4. 重要概念和注意事项

  • getResourceAsStream 方法
    getResourceAsStream()ServletContext 提供的一个方法,用于从 Web 应用的类路径中加载资源文件。这个方法会返回一个 InputStream,可以用来读取文件内容。在这里,它加载的是 WEB-INF/classes/com/kuang/servlet/aa.properties 文件。

  • 路径问题
    路径 /WEB-INF/classes/com/kuang/servlet/aa.properties 表示文件位于 Web 应用根目录下的 WEB-INF/classes/com/kuang/servlet 目录中。Web 应用启动时,WEB-INF/classes 目录下的所有资源会被加入到类路径中,从而能够通过 getServletContext().getResourceAsStream 来访问。

  • Properties
    Properties 类是 Java 提供的一种用于处理属性文件(如 .properties 文件)的类。属性文件是以键值对形式存储配置的文本文件,可以使用 getProperty(key) 方法根据键来获取值。

5. 路径配置

正确的路径设置

  • db.properties 文件放置在 WEB-INF/classes/com/yourapp/config/ 目录下。
  • 使用 getServletContext().getResourceAsStream("/WEB-INF/classes/com/yourapp/config/db.properties") 读取文件时,路径 /WEB-INF/classes/ 是从 Web 应用根目录开始的。

如果您的文件路径不在 WEB-INF/classes/ 目录下,而是放在 WEB-INF/ 或其他路径中,您需要调整路径以正确访问。

6. 补充说明

  • 路径中的前缀 /
    getResourceAsStream() 中,路径前面的 / 表示从 Web 应用的根目录开始查找。/WEB-INF 是 Web 应用的保密目录,通常对外部不可直接访问,但是可以通过 ServletContext 访问。

  • ServletContext 和路径问题
    getResourceAsStream() 方法会从 Web 应用的类路径中查找文件。因此,无论部署在哪里,只要文件位于正确的位置,ServletContext 都能加载到它。

7. 总结

  • 使用 ServletContextgetResourceAsStream() 方法可以加载放置在 WEB-INF/classes 中的资源文件。
  • 通过这种方式,可以避免硬编码绝对路径,确保在开发和生产环境中都能够正确加载资源。
  • 配置文件(如 db.properties)通常会存放在 WEB-INF/classes 中,而通过 Properties 类可以轻松读取和管理这些文件的内容。

会涉及到 properties 类

在 resources 文件夹下创建一个以 .properties 为后缀的文件,并存储以下数据

uername=root
password=123456

在这里插入图片描述

之前的思路如何读取到 db.properties 这个文件下的内容? 通过 properties 类通过绝对地址去 load 出来,但是现在是 web 应用,无法保证绝对地址,是在服务器上获取内容!需要观察它在服务器的什么位置。

下面先通过 maven 的 clean 清理干净(target 文件就会被删除掉),清理完成后,重新启动 tomcat,会产生一个 target 文件,target/servlet-02/WEB-INF/classes/db.properties 下

在这里插入图片描述

先提到一个 classpath,类路径的概念,我们源代码 java 和资源文件 resources 的内容最终都会以相同的路径打包到类路径也就是 classes 文件夹下的
在这里插入图片描述

如果我们在源代码 java 文件而不是资源文件 resources 下创建一个如 test.properties 文件,观察它是否会被打包到 target 目录下
在这里插入图片描述

重启 tomcat,发现并没有生成 test.properties
在这里插入图片描述

实际上在 maven 中已经讲过,需要在 pom.xml 中进行配置,如下,从而打包 java 源码下的 properties 文件

<!--在build中配置resources,来防止我们资源导出失败的问题-->
<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>
  • 在 java 目录中新建 properties
  • 在 resource 目录下新建 properties

发现都被打包到了同一个路径下:classes,我们称这个路径为 classpath

下面我们新建一个 ServletDemo05 类来从服务器上读取我们想要的文件内容

public class ServletDemo05 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        InputStream is = this.getServletContext().getResourceAsStream("/WEB-INF/classes/com/kuang/servlet/aa.properties"); // 获取文件路径

        Properties prop = new Properties();
        prop.load(is);
        String user = prop.getProperty("username");
        String pwd = prop.getProperty("password");

        resp.getWriter().print(user+":"+pwd); // 输入到网页上

    }

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

并注册 servlet ,命名为 sd5

    <servlet>
        <servlet-name>sd5</servlet-name>
        <servlet-class>com.uestc.servlet.ServletDemo04</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>sd5</servlet-name>
        <url-pattern>/sd5</url-pattern>
    </servlet-mapping>

重启 tomcat,请求 sd5:localhost:8090/s2/sd5,如果发生 500 错误,检查文件路径是否正确,正确尝试将 target 文件删除再使用
在这里插入图片描述

成功获取
在这里插入图片描述

6.6 HttpServletResponse

Web 服务器接收到客户端的 http 请求,针对这个请求,分别创建一个代表请求的 HttpServletRequest 对象,代表响应的一个 HttpServletResponse;

  • 如果想要获取客户端请求过来的参数:找 HttpServletRequest
  • 如果要给客户端响应一些信息:找 HttpServletResponse

6.6.1 简单分类

负责向浏览器发送数据的方法

ServletOutputStream getOutputStream() throws IOException;
PrintWriter getWriter() throws IOException;

负责向浏览器发送响应头的方法

    void setCharacterEncoding(String var1);

    void setContentLength(int var1);

    void setContentLengthLong(long var1);

    void setContentType(String var1);

    void setDateHeader(String var1, long var2);

    void addDateHeader(String var1, long var2);

    void setHeader(String var1, String var2);

    void addHeader(String var1, String var2);

    void setIntHeader(String var1, int var2);

    void addIntHeader(String var1, int var2);

响应的状态码

int SC_CONTINUE = 100;
int SC_SWITCHING_PROTOCOLS = 101;
int SC_OK = 200;
int SC_CREATED = 201;
int SC_ACCEPTED = 202;
int SC_NON_AUTHORITATIVE_INFORMATION = 203;
int SC_NO_CONTENT = 204;
int SC_RESET_CONTENT = 205;
int SC_PARTIAL_CONTENT = 206;
int SC_MULTIPLE_CHOICES = 300;
int SC_MOVED_PERMANENTLY = 301;
int SC_MOVED_TEMPORARILY = 302;
int SC_FOUND = 302;
int SC_SEE_OTHER = 303;
int SC_NOT_MODIFIED = 304;
int SC_USE_PROXY = 305;
int SC_TEMPORARY_REDIRECT = 307;
int SC_BAD_REQUEST = 400;
int SC_UNAUTHORIZED = 401;
int SC_PAYMENT_REQUIRED = 402;
int SC_FORBIDDEN = 403;
int SC_NOT_FOUND = 404;
int SC_METHOD_NOT_ALLOWED = 405;
int SC_NOT_ACCEPTABLE = 406;
int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
int SC_REQUEST_TIMEOUT = 408;
int SC_CONFLICT = 409;
int SC_GONE = 410;
int SC_LENGTH_REQUIRED = 411;
int SC_PRECONDITION_FAILED = 412;
int SC_REQUEST_ENTITY_TOO_LARGE = 413;
int SC_REQUEST_URI_TOO_LONG = 414;
int SC_UNSUPPORTED_MEDIA_TYPE = 415;
int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
int SC_EXPECTATION_FAILED = 417;
int SC_INTERNAL_SERVER_ERROR = 500;
int SC_NOT_IMPLEMENTED = 501;
int SC_BAD_GATEWAY = 502;
int SC_SERVICE_UNAVAILABLE = 503;
int SC_GATEWAY_TIMEOUT = 504;
int SC_HTTP_VERSION_NOT_SUPPORTED = 505;

HttpServletResponse 是 Java Servlet API 中的一个接口,代表了服务器端对客户端的响应。它负责向客户端发送响应数据和设置响应头信息。你提到的几种方法可以分为两大类:一类是处理响应数据的发送,另一类是处理响应头的设置。

1. 向浏览器发送数据的方法

这类方法主要用于向客户端发送响应体内容。

ServletOutputStream getOutputStream() throws IOException;
  • 这个方法返回一个 ServletOutputStream 对象,允许你以二进制流的形式写入响应数据。

  • 常用于发送图片、视频、文件等二进制数据。

  • 例如:

    ServletOutputStream out = response.getOutputStream();
    out.write(byteArray); // 直接写入字节数组
    out.flush(); // 确保所有数据都写入
    
PrintWriter getWriter() throws IOException;
  • 这个方法返回一个 PrintWriter 对象,用于以字符流的方式写入响应数据。

  • 通常用于发送文本数据,例如 HTML、JSON 或 XML 格式的响应。

  • 你可以使用 PrintWriterprintln()write() 方法向客户端发送字符数据。

  • 例如:

    PrintWriter writer = response.getWriter();
    writer.println("<html><body><h1>Hello World</h1></body></html>");
    writer.flush(); // 确保数据被发送到客户端
    

2. 向浏览器发送响应头的方法

这些方法用于设置响应的头信息,告知浏览器如何处理响应体的数据。

void setCharacterEncoding(String var1);
  • 设置响应的字符编码。常用的字符编码包括 UTF-8ISO-8859-1 等。

  • 该方法需要在响应数据之前调用,否则可能会导致乱码。

  • 例如:

    response.setCharacterEncoding("UTF-8");
    
void setContentLength(int var1);
  • 设置响应体的长度,以字节为单位。

  • 对于文本响应,可以通过 response.getWriter() 写入数据并自动计算长度,或者手动设置长度。

  • 例如:

    response.setContentLength(1024); // 设置响应体的字节长度为 1024 字节
    
void setContentLengthLong(long var1);
  • 这是 setContentLength 的长整型版本,适用于内容长度大于 2GB 的情况。

  • 例如:

    response.setContentLengthLong(5000000000L); // 设置一个非常大的响应体长度
    
void setContentType(String var1);
  • 设置响应内容的类型,例如 text/htmlapplication/jsonimage/png 等。

  • 通过设置此头信息,客户端会根据类型来解析响应数据。

  • 例如:

    response.setContentType("text/html; charset=UTF-8");
    
void setDateHeader(String var1, long var2);
  • 设置响应头中的日期信息,例如 ExpiresLast-Modified 等。

  • var1 是日期头的名称,var2 是日期的毫秒表示。

  • 例如:

    long currentTime = System.currentTimeMillis();
    response.setDateHeader("Last-Modified", currentTime);
    
void addDateHeader(String var1, long var2);
  • setDateHeader 类似,但此方法会在已有的同名响应头后追加新的日期值,而不是替换。

  • 例如:

    response.addDateHeader("Expires", System.currentTimeMillis() + 3600 * 1000); // 设置一个过期时间
    
void setHeader(String var1, String var2);
  • 设置一个自定义的响应头,var1 为头的名称,var2 为对应的值。

  • 例如:

    response.setHeader("Cache-Control", "no-cache");
    
void addHeader(String var1, String var2);
  • setHeader 类似,但此方法允许向同名响应头追加多个值。

  • 例如:

    response.addHeader("Set-Cookie", "username=JohnDoe");
    response.addHeader("Set-Cookie", "sessionId=abc123");
    
void setIntHeader(String var1, int var2);
  • 设置一个整数类型的响应头。

  • 例如:

    response.setIntHeader("Retry-After", 3600); // 设置重试的时间间隔为 3600 秒
    
void addIntHeader(String var1, int var2);
  • setIntHeader 类似,但此方法允许向同名响应头追加多个整数值。

  • 例如:

    response.addIntHeader("Warning", 298); // 添加一个警告头
    

总结

  • 向浏览器发送数据的方法(如 getOutputStream()getWriter())用于传输响应体的内容。
  • 设置响应头的方法(如 setCharacterEncoding()setContentType() 等)用于配置响应的元数据,例如字符集、内容类型和缓存策略等。

6.6.2 下载文件

给浏览器输出消息

下载文件

  1. 要获取下载文件的路径
  2. 下载的文件名是什么?
  3. 设置浏览器使其支持下载的内容
  4. 获取下载文件的输入流
  5. 创建缓冲区
  6. 获取 OutputStream 对象
  7. 将 FileOutputStream 写入到 buffer 缓冲区
  8. 使用 OutputStream 将缓冲区中的数据输出到客户端

新建一个子工程为 response
在这里插入图片描述

添加 java、resources 文件,以及修改 web.xml
在这里插入图片描述

在 resources 文件下,随意加入一个图片,命名为 1.png
在这里插入图片描述

创建类 FileServlet

public class FileServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1. 要获取下载文件的路径
        String realPath = "/1.png";
        System.out.println("下载文件的路径:"+realPath);

        // 2. 下载的文件名是啥?
        String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1); // 截取项目名

        // 3. 设置想办法让浏览器能够支持(Content-Disposition)下载我们需要的东西,中文文件名URLEncoder.encode编码,否则有可能乱码
        resp.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(fileName,"UTF-8"));

        // 4. 获取下载文件的输入流
        FileInputStream in = new FileInputStream(realPath);

        // 5. 创建缓冲区
        int len = 0;
        byte[] buffer = new byte[1024];

        // 6. 获取OutputStream对象
        ServletOutputStream out = resp.getOutputStream();

        // 7. 将FileOutputStream流写入到buffer缓冲区,使用OutputStream将缓冲区中的数据输出到客户端!
        while ((len=in.read(buffer))>0){
            out.write(buffer,0,len); // 写到缓冲区中
        }

        //流关闭
        in.close();
        out.close();
    }

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

注册Servlet

<servlet>
  <servlet-name>filedown</servlet-name>
    <servlet-class>com.uestc.servlet.FileServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>filedown</servlet-name>
    <url-pattern>/down</url-pattern>
</servlet-mapping>

修改 tomcat 配置,选择 response:war
在这里插入图片描述

重启 tomcat,请求 down,发生了如下错误:
在这里插入图片描述

查看后台输出的路径结果,发现路径是 D 盘下的,肯定不对
在这里插入图片描述

获取 1.png 的绝对路径,我的为:D:\08Code\Servlet\response\target\classes\1.png
在这里插入图片描述

因此更新代码,更改地址
在这里插入图片描述

然后 clean maven 项目后重启 tomcat,再请求 down ,成功!
在这里插入图片描述

有几点可以优化和注意的地方:

1. 文件路径问题

realPath 目前是硬编码的路径("/1.png"),这意味着文件是从根目录中获取的。如果文件存在于项目的某个特定目录中,建议使用 ServletContext.getRealPath() 获取文件的绝对路径。例如:

String realPath = getServletContext().getRealPath("/WEB-INF/files/1.png");  

这会返回实际存储文件的路径,"/WEB-INF/files/1.png" 是相对路径。

2. 文件路径分隔符

你的代码使用了 realPath.substring(realPath.lastIndexOf("\\") + 1) 来提取文件名,这在 Windows 环境下是有效的。但为了跨平台,建议使用 File.separator 来保证路径分隔符正确:

String fileName = realPath.substring(realPath.lastIndexOf(File.separator) + 1);  

3. 下载文件时的 Content-Type

在返回文件之前,最好设置文件的 Content-Type 头,告知浏览器如何处理文件。对于图片文件,可以使用如下设置:

resp.setContentType("image/png");  

如果文件类型是动态的,可以根据文件后缀设置不同的 MIME 类型。可以用一个工具类来根据文件扩展名返回合适的 MIME 类型。

4. 错误处理

当前代码中没有异常处理逻辑,建议添加文件不存在或读取错误时的处理逻辑。例如:

File file = new File(realPath);  
if (!file.exists()) {  
    resp.setStatus(HttpServletResponse.SC_NOT_FOUND); // 404错误  
    return;  
}  

5. 优化流的关闭

虽然 Java 7 引入了 try-with-resources 语法来自动关闭流,你可以使用它来避免手动关闭流。这样可以确保即使出现异常也能正确关闭流:

try (FileInputStream in = new FileInputStream(realPath);  
     ServletOutputStream out = resp.getOutputStream()) {  
    int len = 0;  
    byte[] buffer = new byte[1024];  
    while ((len = in.read(buffer)) > 0) {  
        out.write(buffer, 0, len);  
    }  
} catch (IOException e) {  
    e.printStackTrace();  
    resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); // 500错误  
}  

6. 文件大小

如果文件非常大,你可能希望设置 Content-Length 头,这样浏览器可以知道文件的大小。例如:

File file = new File(realPath);  
resp.setContentLengthLong(file.length());  

7. doPost 方法

你重写了 doPost 方法,但是没有做任何事情。通常如果不需要处理 POST 请求,可以不实现该方法,或者抛出一个异常来阻止 POST 请求。

@Override  
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {  
    resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "POST method is not supported.");  
}  

综合优化后的代码示例:

import javax.servlet.ServletException;  
import javax.servlet.ServletOutputStream;  
import javax.servlet.http.HttpServlet;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
import java.io.*;  
import java.net.URLEncoder;  
import java.nio.file.Files;  
  
public class FileServlet extends HttpServlet {  
    @Override  
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {  
        // 获取文件的实际路径  
        String realPath = getServletContext().getRealPath("/WEB-INF/files/1.png");  
        File file = new File(realPath);  
        
        if (!file.exists()) {  
            resp.setStatus(HttpServletResponse.SC_NOT_FOUND);  
            return;  
        }  
  
        // 设置文件名  
        String fileName = realPath.substring(realPath.lastIndexOf(File.separator) + 1);  
  
        // 设置 Content-Disposition 让浏览器进行下载  
        resp.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));  
  
        // 设置Content-Type(根据文件类型进行设置)  
        String mimeType = getServletContext().getMimeType(realPath);  
        resp.setContentType(mimeType != null ? mimeType : "application/octet-stream");  
  
        // 设置Content-Length  
        resp.setContentLengthLong(file.length());  
  
        try (FileInputStream in = new FileInputStream(realPath);  
             ServletOutputStream out = resp.getOutputStream()) {  
            // 创建缓冲区  
            byte[] buffer = new byte[1024];  
            int len;  
            while ((len = in.read(buffer)) > 0) {  
                out.write(buffer, 0, len);  
            }  
        } catch (IOException e) {  
            e.printStackTrace();  
            resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); // 500错误  
        }  
    }  
  
    @Override  
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {  
        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "POST method is not supported.");  
    }  
}  

6.6.3 验证码功能实现

验证怎么来的?

  • 前端实现
  • 后端实现,需要用到 java 的图片类,生成一个图片,没有图片就没有验证码,我们要把这个图片响应到前端

在上述 response 文件的 java 源码下,创建一个 ImageServlet 类

public class ImageServlet extends HttpServlet {

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

        //如何让浏览器3秒自动刷新一次;
        resp.setHeader("refresh","3");
        
        //在内存中创建一个图片
        BufferedImage image = new BufferedImage(80,20,BufferedImage.TYPE_INT_RGB);
        //得到图片
        Graphics2D g = (Graphics2D) image.getGraphics(); //笔
        //设置图片的背景颜色
        g.setColor(Color.white);
        g.fillRect(0,0,80,20);
        //给图片写数据
        g.setColor(Color.BLUE);
        g.setFont(new Font(null,Font.BOLD,20));
        g.drawString(makeNum(),0,20);

        //告诉浏览器,这个请求用图片的方式打开
        resp.setContentType("image/jpeg");
        //网站存在缓存,不让浏览器缓存
        resp.setDateHeader("expires",-1);
        resp.setHeader("Cache-Control","no-cache");
        resp.setHeader("Pragma","no-cache");

        //把图片写给浏览器
        ImageIO.write(image,"jpg", resp.getOutputStream());

    }

    //生成随机数
    private String makeNum(){
        Random random = new Random();
        String num = random.nextInt(9999999) + "";
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 7-num.length() ; i++) {
            sb.append("0");
        }
        num = sb.toString() + num;
        return num;
    }


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

注册 Servelt

    <servlet>
        <servlet-name>ImageServlet</servlet-name>
        <servlet-class>com.uestc.servlet.ImageServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ImageServlet</servlet-name>
        <url-pattern>/ImageServlet</url-pattern>
    </servlet-mapping>

然后走 ImageServlet 这个请求,成功产生,并且每 3s 产生一次
在这里插入图片描述

6.6.4 实现重定向

在这里插入图片描述

Web 资源 B 收到客户端 A 请求后,通知 A 访问另一个 Web 资源 C ,这个过程叫做重定向

常见场景:

  • 用户登录,登录成功就会访问另一个页面

重定向是方法 sendRedirect 来实现

void sendRedirect(String var1) throws IOException;

创建类 RedirectServlet,使其重定位到上面我们注册的 ImageServlet 里

public class RedirectServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*
        resp.setHeader("Location","/response/ImageServlet");
        resp.setStatus(302);
         */
        resp.sendRedirect("/reseponse/ImageServlet");//重定向
    }

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

注册 Servlet

<servlet>
<servlet-name>RedirectServlet</servlet-name>
    <servlet-class>com.uestc.servlet.RedirectServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>RedirectServlet</servlet-name>
    <url-pattern>/red</url-pattern>
</servlet-mapping>

启动tomcat,请求 red,成功重定向到之前的验证码 ImageServlet 中,注意请求时加上我们设置的入口 reseponse,不然进入的是 localhost:8090/ImageServlet,少了 reseponse
在这里插入图片描述

面试题:重定向与转发的区别?

相同点:

  • 页面都会实现跳转

不同点:

  • 请求转发的时候,URL 不会发生变化
  • 重定向时候,URL 地址栏会发生变化;

6.6.4 简单实现登录重定向

index.jsp (注意 maven 下导入 JSP 的包)

<html>
	<body>
		<h2>Hel1o World!</h2>
		
		<%--这里提交的路径,需要寻找到项目的路径--%>
		<%--${pageContext. request, contextPath}代表当前的项目--%>
		<form action="${pageContext. request.contextPath}/login" method="get">
			用户名: <input type="text" name="username"> <br>
			密码: <input type="password" name="password"> <br>
			<input type="submit">
		</form>
	
	</body>
</html>

关键部分解释:

${pageContext.request.contextPath}

这是 JSP 表达式语言(Expression Language, EL)中的一个表达式,用于动态获取当前项目的上下文路径。具体含义如下:

  • pageContext

    • 是一个隐式对象,提供对页面范围内相关信息的访问。
    • 它封装了许多有用的属性,比如 requestresponse 等。
  • request

    • 是另一个隐式对象,表示当前的 HTTP 请求。
  • contextPath

    • request 对象的一个属性,返回当前 Web 应用程序的上下文路径。
    • 通常对应 Web 应用部署时的根路径,比如 /myApp

创建一个类 RequestTest.java

public class RequestTest 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 {
        super.doPost(req, resp);
    }
}

注册 Servlet

<servlet>
  <servlet-name>requset</servlet-name>
  <servlet-class>com.uestc.servlet.RequestTest</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>requset</servlet-name>
  <url-pattern>/login</url-pattern>
</servlet-mapping>

重启 Tomcat,请求 login,乱码是浏览器的问题,不是我们代码的问题,成功
在这里插入图片描述
但是输入账号,密码后,点击提交,反应到了浏览器的地址中
在这里插入图片描述

下面我们来处理上述的请求,即提交后跳转页面。

新建一个 success.jsp,内容只有一个 success 输出

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1> success </h1>

</body>
</html>

然后获取 RequestTest 获取账号密码,打印到后台,并跳转到 success.jsp

public class RequestTest extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //处理请求
        String username = req.getParameter("username");
        String password = req.getParameter("password");

        System.out.println(username+":"+password);

        //重定向时候一定要注意,路径问题,否则404;
        resp.sendRedirect("/response/success.jsp");
    }

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

现在重启 Tomcat,后台获得了这个账号密码,并打印输出,并且跳转到了 success.jsp
在这里插入图片描述
在这里插入图片描述

6.7、HttpServletRequest

HttpServletRequest 代表客户端的请求,用户通过 HTTP 协议访问服务器,HTTP 请求中的所有信息会被封装到 HttpServletRequest ,通过这个 HttpServletRequest 的方法,获得客户端的所有信息;

在这里插入图片描述

获取前端传递的参数,请求转发,下面是四个获取参数的方法,重点掌握两个
在这里插入图片描述

下面新建一个子工程,request
在这里插入图片描述

添加 java 文件夹,resources 文件夹,进行标记,并更新 web.xml
在这里插入图片描述

并删除 index.jsp,再新建一个 index.jsp,会多一个头
在这里插入图片描述

创建类 LoginServlet,前面使用的都是 get 方法,下面使用 post 方法
在这里插入图片描述

在 index.jsp 中写入一个简单的前端,注意下面的 <form action="<%=request.getContextPath()%>/login" method="get"> 中的 method=“get” 在提交登录后会去调用 get 方法

<%--
  Created by IntelliJ IDEA.
  User: 15592
  Date: 2024/7/25
  Time: 17:46
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录</title>
</head>
<body>

<h1> 登录成功 </h1>
<div style="text-align: center">
    <form action="<%=request.getContextPath()%>/login" method="get">
        用户名: <input type="text" name="username"> </input> <br>
        密码: <input type="password" name="password"> </input> <br>
        爱好:
        <input type="checkbox" name="hobbys" value="女孩"> 女孩
        <input type="checkbox" name="hobbys" value="代码"> 代码
        <input type="checkbox" name="hobbys" value="唱歌"> 唱歌
        <input type="checkbox" name="hobbys" value="电影"> 电影

        <br>
        <input type="submit" value="登录">
    </form>

</div>

</body>
</html>

把前面的 success.jsp 拿过来

<%--
  Created by IntelliJ IDEA.
  User: 15592
  Date: 2024/7/25
  Time: 17:33
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1> success </h1>

</body>
</html>

在这里插入图片描述

LoginServlet 类中获取 index.jsp 的内容(注意与前面的 method=“get”匹配,执行下面 doGet 方法)

public class LoginServlet extends HttpServlet {

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

        req.setCharacterEncoding("utf-8"); // 解决后端乱码问题
        resp.setCharacterEncoding("utf-8"); // 解决输入到页面乱码问题

        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String[] hobbys = req.getParameterValues("hobbys");
        System.out.println("=============================");
        //后台接收中文乱码问题
        System.out.println(username);
        System.out.println(password);
        System.out.println(Arrays.toString(hobbys));
        System.out.println("=============================");


        System.out.println(req.getContextPath());
        //通过请求转发
        //这里的 / 代表当前的web应用
        req.getRequestDispatcher("/success.jsp").forward(req,resp);

    }


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

注册 Servlet

<servlet>
    <servlet-name>requset</servlet-name>
    <servlet-class>com.uestc.servlet.LoginServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>requset</servlet-name>
    <url-pattern>/login</url-pattern>
</servlet-mapping>

由于新建了 request 子工程,需要重新配置 tomcat
在这里插入图片描述

启动 tomcat
在这里插入图片描述

点击登录后成功跳转
在这里插入图片描述
在这里插入图片描述

References

B站—【狂神说Java】JavaWeb入门到实战—笔记

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

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

相关文章

C语言性能优化:从基础到高级的全面指南

引言 C 语言以其高效、灵活和功能强大而著称&#xff0c;被广泛应用于系统编程、嵌入式开发、游戏开发等领域。然而&#xff0c;要写出高性能的 C 语言代码&#xff0c;需要对 C 语言的特性和底层硬件有深入的了解。本文将详细介绍 C 语言性能优化的背后技术&#xff0c;并通过…

C语言-数据结构-查找

目录 一,查找的概念 二,线性查找 1,顺序查找 2,折半查找 3,分块查找 三,树表的查找 1,二叉排序树 (1)查找方式: (2)、二叉排序树的插入和生成 (3)、二叉排序树的删除 2,平衡二叉树 (1)、什么是平衡二叉树 (2)、平衡二叉树的插入调整 &#xff08;1&#xff09;L…

[江科大编程技巧] 第1期 定时器实现非阻塞式程序 按键控制LED闪烁模式——笔记

提前声明——我只是写的详细其实非常简单&#xff0c;不要看着多就放弃学习&#xff01; 阻塞&#xff1a;执行某段程序时&#xff0c;CPU因为需要等待延时或者等待某个信号而被迫处于暂停状态一段时间&#xff0c;程序执行时间较长或者时间不定 非阻塞&#xff1a;执行某段程…

如何理解:产品线经营管理的战略、组织、业务、项目、流程、绩效之间的逻辑关系?-中小企实战运营和营销工作室博客

如何理解&#xff1a;产品线经营管理的战略、组织、业务、项目、流程、绩效之间的逻辑关系&#xff1f;-中小企实战运营和营销工作室博客 产品线经营管理中&#xff0c;战略、组织、业务、项目、流程、绩效之间存在着紧密的逻辑关系&#xff0c;它们相互影响、相互作用&#xf…

【CSS in Depth 2 精译_096】16.4:CSS 中的三维变换 + 16.5:本章小结

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第五部分 添加动效 ✔️【第 16 章 变换】 ✔️ 16.1 旋转、平移、缩放与倾斜 16.1.1 变换原点的更改16.1.2 多重变换的设置16.1.3 单个变换属性的设置 16.2 变换在动效中的应用 16.2.1 放大图标&am…

Oracle 11G还有新BUG?ORACLE 表空间迷案!

前段时间遇到一个奇葩的问题&#xff0c;在开了SR和oracle support追踪两周以后才算是有了不算完美的结果&#xff0c;在这里整理出来给大家分享。 1.问题描述 12/13我司某基地MES全厂停线&#xff0c;系统卡死不可用&#xff0c;通知到我排查&#xff0c;查看alert log看到是…

三只脚的电感是什么东西?

最近在做加湿器&#xff0c;把水雾化的陶瓷片需要有专门的驱动电路。 我参考了某宝卖家的驱动板以及网上的开源项目&#xff0c;发现了驱动电路的核心就是一个三脚电感。 在此之前我都没注意过这玩意&#xff0c;三脚电感不也还是电感嘛&#xff1f; 今天我们就来看看三脚电…

pyQT + OpenCV相关练习

一、设计思路 1、思路分析与设计 本段代码是一个使用 PyQt6 和 OpenCV 创建的图像处理应用程序。其主要功能是通过一个图形界面让用户对图片进行基本的图像处理操作&#xff0c;如灰度化、翻转、旋转、亮度与对比度调整&#xff0c;以及一些滤镜效果&#xff08;模糊、锐化、边…

【Git_bugs】remote error GH013 Repository rule violations found for.md

背景 1 在一个分支上的提交顺序如下&#xff1a;-> 代表新的提交 在提交 E 中&#xff0c;文件包含了 GitHub 生成的 token提交 F 是一次普通的提交&#xff0c;不包含 token A -> ... -> E -> F (敏感信息在 E 中)附&#xff1a;给提交起名是为了方便说明问题。…

Day1 微服务 单体架构、微服务架构、微服务拆分、服务远程调用、服务注册和发现Nacos、OpenFeign

目录 1.导入单体架构项目 1.1 安装mysql 1.2 后端 1.3 前端 2.微服务 2.1 单体架构 2.2 微服务 2.3 SpringCloud 3.微服务拆分 3.1 服务拆分原则 3.1.1 什么时候拆 3.1.2 怎么拆 3.2 拆分购物车、商品服务 3.2.1 商品服务 3.2.2 购物车服务 3.3 服务调用 3.3.1 RestTemplate 3.…

安卓执法仪Android接入国标GB28181平台实现实时监控、对讲、报警、定位等管理方案

最近协助不少企业完成了4G无线设备国标接入的需求&#xff0c;尤其是国产芯片的接入&#xff0c;国标发展了十年的时间&#xff0c;目前协议从完成度、性能、AI等各个方面&#xff0c;都已经非常完美地满足各种各样的场景需求&#xff0c;尤其是GB28181-2022的推出&#xff0c;…

SpringMVC学习(二)——RESTful API、拦截器、异常处理、数据类型转换

一、RESTful (一)RESTful概述 RESTful是一种软件架构风格&#xff0c;用于设计网络应用程序。REST是“Representational State Transfer”的缩写&#xff0c;中文意思是“表现层状态转移”。它基于客户端-服务器模型和无状态操作&#xff0c;以及使用HTTP请求来处理数据。RES…

国内独立开发者案例及免费送独立开发蓝图书

独立开发者在国内越来越受到关注&#xff0c;他们追求的是一种自由且自给自足的工作状态。 送这个&#xff1a; 少楠light&#xff08;Flomo、小报童、如果相机&#xff09;&#xff1a;他们是独立开发者的典范&#xff0c;不仅开发了多款产品&#xff0c;还坚信“剩者为王”…

【JavaEE进阶】@RequestMapping注解

目录 &#x1f4d5;前言 &#x1f334;项目准备 &#x1f332;建立连接 &#x1f6a9;RequestMapping注解 &#x1f6a9;RequestMapping 注解介绍 &#x1f384;RequestMapping是GET还是POST请求&#xff1f; &#x1f6a9;通过Fiddler查看 &#x1f6a9;Postman查看 …

一文详解MacOS+CLion——构建libtorch机器学习开发环境

对于希望在本地环境中进行深度学习开发的开发者来说&#xff0c;配置合适的工具链是至关重要的一步。本文旨在帮助您在 macOS 操作系统上&#xff0c;利用 CLion IDE 和 PyTorch 的 C依赖库——libtorch&#xff0c;快速搭建起一个高效的开发环境。这里我们将一步步地讲解如何下…

Bert中文文本分类

这是一个经典的文本分类问题&#xff0c;使用google的预训练模型BERT中文版bert-base-chinese来做中文文本分类。可以先在Huggingface上下载预训练模型备用。https://huggingface.co/google-bert/bert-base-chinese/tree/main 我使用的训练环境是 pip install torch2.0.0; pi…

shardingsphere分库分表项目实践5-自己用java写一个sql解析器+完整项目源码

前1节我们介绍了 shardingsphere 分表分库的sql解析与重写&#xff1a; shardingsphere分库分表项目实践4-sql解析&重写-CSDN博客 那么shardingsphere sql 解析底层究竟是怎么实现的呢&#xff0c;其实它直接用了著名的开源软件 antlr . antlr 介绍&#xff1a; ANTLR&a…

10分钟掌握项目管理核心工具:WBS、甘特图、关键路径法全解析

一、引言 在项目管理的广阔天地里&#xff0c;犹如一场精心编排的交响乐演奏&#xff0c;每个乐器、每个音符都需精准配合才能奏响美妙乐章。而 WBS&#xff08;工作分解结构&#xff09;、甘特图、关键路径法无疑是这场交响乐中的关键乐章&#xff0c;它们从不同维度为项目管…

【LLM】OpenAI 的DAY12汇总和o3介绍

note o3 体现出的编程和数学能力&#xff0c;不仅达到了 AGI 的门槛&#xff0c;甚至摸到了 ASI&#xff08;超级人工智能&#xff09;的边。 Day 1&#xff1a;o1完全版&#xff0c;开场即巅峰 12天发布会的开场即是“炸场级”更新——o1完全版。相比此前的预览版本&#x…

使用Kubernetes部署MySQL+WordPress

目录 前提条件 部署MySQL和WordPress 编写yaml文件 应用yaml文件 存在问题及解决方案 创建PV(持久化卷) 创建一个PVC(持久化卷声明) 部署添加PVC 查看PV对应的主机存储 删除资源 查看资源 删除deployment和service 查看主机数据 删除PVC和PV 删除主机数据 前提条…