JavaWeb(一) | 基本概念(web服务器、Tomcat、HTTP、Maven)、Servlet 简介
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
- 它无法和数据库交互(数据无法持久化,用户无法交互)
- Web页面无法动态更新,所有用户看到都是同一个页面
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:
- 安装 or 解压
- 了解配置文件及目录结构
- 这个东西的作用
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/
可能遇到的问题:
- Java环境变量没有配置
- 闪退问题:需要配置兼容性
- 乱码问题:配置文件中设置
可以修改 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">
高难度面试题:
请你谈谈网站是如何进行访问的!
- 输入一个域名,如 www.baidu.com 或者 localhost:8080 等,回车
- 检查本机的 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
- 构建一个普通的 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
- 关于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
- 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 ,并标记为 源代码文件和资源文件
- 编写一个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);
}
}
- 编写 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>
- 配置 Tomcat
注意: 配置项目发布的路径就可以了
配置好后,解决警告,点击 Fix
会进入到 Deployment,选择项目 servlet-01:war
或者点击 + 号,选择则 artifacts,再设置,其中 Application context 设置为 /s1,表示请求
- 启动测试,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 问题
- Mapping 是请求映射的一个路径,一个 Servlet 指定一个映射路径的情况:
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
- 一个 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>
- 一个Servlet指定通用映射路径,使用 /*,这个通配符
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello/*</url-pattern>
</servlet-mapping>
- 默认请求路径(就是什么都不写,会走如下请求)
<!--默认请求路径-->
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
- 指定一些后缀或者前缀等等….
<!--可以自定义后缀实现请求映射
注意点,*前面不能加项目映射的路径
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
。
- 优先级问题
指定了固有的映射路径优先级最高,如果找不到就会走默认的处理请求;
<!--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 共享数据、访问应用参数、获取资源、日志记录等功能。主要功能:
- 共享资源:通过
setAttribute()
和getAttribute()
方法,多个 Servlet 可以共享数据。- 访问应用参数:通过
getInitParameter()
获取web.xml
中的初始化参数。- 资源访问:通过
getResource()
和getResourceAsStream()
获取应用中的静态资源。- 日志记录:使用
log()
方法记录应用日志。- 获取服务器信息:使用
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>
:这是参数的值,通常是一个字符串,可以是任意的配置信息。典型的使用场景
数据库连接配置:
在 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>
文件路径或配置路径:
对于一些需要特定文件路径或者配置文件路径的情况,也可以通过<context-param>
来传递这些路径信息。<context-param> <param-name>config.file.path</param-name> <param-value>/path/to/config.xml</param-value> </context-param>
外部服务或 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. 在
Servlet
或Filter
中使用在
Servlet
或Filter
中也可以轻松访问这些参数。比如:@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 页面等),请求会继续在服务器端处理,而客户端并不会知道这个过程。转发后,控制权交给目标资源,原请求的request
和response
会被传递给目标资源。include(ServletRequest request, ServletResponse response)
:将请求的响应内容包含在另一个资源的响应中。通常是将另一个Servlet
或 JSP 页面的内容插入到当前页面中。3.
context.getRequestDispatcher("/gp").forward(req, resp)
解释context.getRequestDispatcher("/gp").forward(req, resp);
context
: 这里的context
是ServletContext
对象,它表示应用的上下文。可以通过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. 请求转发的特点
- 服务器端操作:请求转发完全由服务器处理,客户端(浏览器)并不知道请求已经被转发。
- 共享请求和响应对象:在转发过程中,原始的
request
和response
对象会被传递给目标资源。因此,转发后的资源可以访问到原始请求中的所有参数、属性等。- URL 不变:请求转发后,客户端浏览器的 URL 不会发生变化,仍然是最初请求的 URL。这是请求转发与重定向的一个主要区别。
5. 请求转发的应用场景
请求转发通常用于以下几种情况:
- 请求链处理:
- 在 Web 应用中,一个请求可能需要经过多个
Servlet
或资源的处理。例如,一个请求在一个Servlet
中进行验证或设置了数据,然后转发到另一个Servlet
或 JSP 页面处理结果并展示给用户。- 示例:用户登录后,先由一个
Servlet
进行身份验证,然后将验证结果转发到一个显示用户信息的页面。
- JSP 页面之间的跳转:
- 一个
Servlet
或一个 JSP 页面可以通过请求转发将请求传递给另一个 JSP 页面,生成最终的响应。- 示例:一个
Servlet
处理了表单提交的逻辑后,将请求转发到一个 JSP 页面显示结果。
- 分布式 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. 总结
- 请求转发是服务器端的操作,客户端无法察觉,适用于在服务器端处理多个资源之间的请求。
- 通过
RequestDispatcher
的forward()
方法实现请求的转发。- 请求转发不会改变浏览器 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/classes
或WEB-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); } }
解释
加载配置文件:
getServletContext().getResourceAsStream("/WEB-INF/classes/com/yourapp/config/db.properties")
:
- 这行代码通过
ServletContext
来加载db.properties
配置文件。路径/WEB-INF/classes/com/yourapp/config/db.properties
表示相对于 Web 应用的根路径。getResourceAsStream()
会返回一个输入流InputStream
,您可以用它来读取文件内容。读取文件内容:
- 使用
Properties
类来读取.properties
文件的键值对。prop.load(is)
会从InputStream
加载配置项。- 通过
prop.getProperty("db.url")
、prop.getProperty("db.username")
和prop.getProperty("db.password")
获取数据库连接的相关配置。返回响应:
- 将数据库的配置信息输出到网页上,使用
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. 总结
- 使用
ServletContext
的getResourceAsStream()
方法可以加载放置在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 格式的响应。
你可以使用
PrintWriter
的println()
或write()
方法向客户端发送字符数据。例如:
PrintWriter writer = response.getWriter(); writer.println("<html><body><h1>Hello World</h1></body></html>"); writer.flush(); // 确保数据被发送到客户端
2. 向浏览器发送响应头的方法
这些方法用于设置响应的头信息,告知浏览器如何处理响应体的数据。
void setCharacterEncoding(String var1);
设置响应的字符编码。常用的字符编码包括
UTF-8
、ISO-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/html
、application/json
、image/png
等。通过设置此头信息,客户端会根据类型来解析响应数据。
例如:
response.setContentType("text/html; charset=UTF-8");
void setDateHeader(String var1, long var2);
设置响应头中的日期信息,例如
Expires
、Last-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 下载文件
给浏览器输出消息
下载文件
- 要获取下载文件的路径
- 下载的文件名是什么?
- 设置浏览器使其支持下载的内容
- 获取下载文件的输入流
- 创建缓冲区
- 获取 OutputStream 对象
- 将 FileOutputStream 写入到 buffer 缓冲区
- 使用 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
:
- 是一个隐式对象,提供对页面范围内相关信息的访问。
- 它封装了许多有用的属性,比如
request
、response
等。
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入门到实战—笔记