Servlet入门

内容概述

  • JavaEE(Java Enterprise Edition),Java企业版,是一个用于企业级web开发(不需要使用控制台)平台。最早由Sun公司定制并发布,后由Oracle负责维护。
  • JavaEE平台规范了在开发企业级web应用中的技术标准。
  • 在JavaEE平台共包含了13个技术规范(随着JavaEE版本的变化所包含的技术点的数量会有增多)。它们分别是:JDBC、JNDI、EJB、RMI、Servlet、JSP、XML、JMS、Java IDL、JPA、JTA、JavaMail和JAF。

Servlet

浏览器和服务器的交互模式

HTTP-06

HTTP-07

客户端

​ 浏览器

服务端

​ 服务器(高性能的计算机相对于普通的PC来说,服务器在稳定性、安全性、性能等方面有更高的要求)

请求

​ 客户端发送数据给服务端的过程

响应

​ 服务端返回数据给客户端的过程

HTTP协议

​ 客户端请求和响应的标准协议

HTTP协议

​ HTTP 协议(Hypertext Transfer Protocol, 超文本传输协议),是一个客户端请求和响应的标准协议,这个协议详细规定了浏览器和万维网服务器之间互相通信的规则。用户输入地址和端口号之后就可以从服务器上取得所需要的网页信息。

​ 通信规则规定了客户端发送给服务器的内容格式,也规定了服务器发送给客户端的内容格式。客户端发送给服务器的格式叫”请求协议“;服务器发送给客户端的格式叫”响应协议“。

在浏览器中 F12可查看

HTTP-01

浏览器中的书写格式

​ 服务器端资源需要通过浏览器进行访问,此时由浏览器将我们给出的请求解析为满足 HTTP 协议的格式并发出。我们发出的请求格式需要按照浏览器规定的格式来书写,在浏览器中书写格式如下:HTTP-02

​ 当浏览器获取到信息以后,按照特定格式解析并发送即可。浏览器接收到服务器端给出的响应时,也按照 HTTP 协议进行解析获取到各个数据,最后按照特定格式展示给用户。

HTTP协议的特点

  1. 支持客户/服务器模式。

    一次请求: (1)客户端、服务器建立连接

    ​ (2)请求遵照http协议

    ​ (3)响应遵照http协议

    ​ (4)客户端、服务器关闭

  2. 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的 有 GET、POST。每种方法规定了客户与服务器联系的类型不同。由于 HTTP 协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。

  3. 灵活:HTTP 允许传输任意类型的数据对象。传输的类型由Content-Type加以标记。

  4. 无连接:无连接是表示每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。

    PS:无连接——-举例A给B电话,一次通话只说 一件事。好处:没有一直占着资源通道。连接一次就关闭通道,有新的连接再建立通道。缺点:请求频繁的话,建立和释放 TCP 连接的开销就很大。

    HTTP1.1 版本后支持可持续连接。通过这种连接,就有可能在建立一个 TCP 连接后,发送请求并得到回应,然后发送更多的请求并得到更多的回应.通过把一次建立和释放 TCP 连接的开销分摊到多个请求上,则对于每个请求而言,由于 TCP 而造成的相对开销被大大地降低了。而且, 还可以发送流水线请求,也就是说在发送请求 1 之后的回应到来之前就可以发送请求 2.也可以认为,一次连接发送多个请求,由客户机确认是否关闭连接,而服务器会认为这些请求分别来自不同的客户端。

    PS:可持续连接——-举例A给B电话,一次通话说n件事。

  5. 无状态:HTTP 协议是无状态协议。无状态是指协议对于事务处理没有记忆能力,它只是规定了协议而已。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送 的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

    PS:A让B带一份饭,请求发送完就结束了,不会对这个处理做记忆。下次A让B再带一份水,而不是说:给我带一份饭再加一瓶水,如果每次都需要处理前面的信息,那么信息重传会导致传送 的数据量增大,不需要处理之前的逻辑响应肯定也会更快。

HTTP协议之URL

​ HTTP(超文本传输协议)是一个基于请求与响应模式的、应用层的协议,常基于 TCP 的连接方式,绝大多数的 Web 开发,都是构建在 HTTP 协议之上的 Web 应用。

​ HTTP URL (URL 是一种特殊类型的 URI,包含了用于查找某个资源的足够的信息)的格式 如下:

1
http://host[:port]/[abs_path]
1
http://IP(主机名/域名):端口/访问的资源路径
  • http 表示要通过 HTTP 协议来定位网络资源;
  • host 表示合法的 Internet 主机域名或 者 IP 地址;eg:本机地址可以用localhost或者127.0.0.1
  • port 指定一个端口号,为空则使用缺省端口 80;eg:tomcat默认端口号8080
  • abs_path 指定请求资源的 URI; 如果 URL 中没有给出 abs_path,那么当它作为请求 URI 时,必须以“/”的形式给出,通常 这个工作浏览器自动帮我们完成。
  • 请求参数利用?拼接,多个参数用&相连。

HTTP协议之请求

浏览器向服务器发送请求种类

​ 1、浏览器输入URL地址
​ 2、表单提交
​ 3、超链接

HTTP 请求由三部分组成

分别是:请求行、请求头、请求体(请求方式中:get方式没有、post方式有)。

通过chrome浏览器, F12 —> Network查看。

HTTP-13

请求行

请求行以一个方法符号开头,以空格分开,后面跟着请求的 URI 和协议的版本。

​ 格式如下:Method Request-URI HTTP-Version CRLF

​ Method 表示请求方法;

​ Request-URI 是一个统一资源标识符;

​ HTTP-Version 表示请 求的 HTTP 协议版本;

​ CRLF 表示回车和换行;

请求头

​ 请求头用于说明是谁或什么在发送请求、请求源于何处,或者客户端的喜好及能力。服务器可以根据请求头部给出的客户端信息,试着为客户端提供更好的响应。

​ 请求头中信息的格式为key:value。

  • Host
    客户端指定自己想访问的WEB服务器的域名/IP 地址和端口号。
  • Connection
    连接方式。如果值是close则表示基于短连接方式,如果该值是keep-alive,网络连接就是持久的,在一定时间范围内不会关闭,使得对同一个服务器的请求可继续在该连接上完成。
  • Upgrade-Insecure-Requests
    服务端是否支持https加密协议。
  • Cache-Control
    指定请求和响应遵循的缓存机制。
  • User-Agent
    浏览器表明自己的身份(是哪种浏览器)。例如Chrome浏览器:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36。
  • Accept
    告诉WEB服务器自己接受什么介质类型,/ 表示任何类型,type/* 表示该类型下的所有子类型。
  • Accept-Encoding
    浏览器申明自己接收的编码方法,通常指定压缩方法,是否支持压缩,支持什么压缩方法(gzip,deflate)。
  • Accept-Language
    浏览器申明自己接收的语言。语言跟字符集的区别:中文是语言,中文有多种字符集,比如big5,gb2312,gbk等。
  • Accept-Charset
    浏览器告诉服务器自己能接收的字符集。
  • Referer
    浏览器向WEB 服务器表明自己是从哪个网页URL获得点击当前请求中的网址/URL。
  • Refresh
    表示浏览器应该在多少时间之后刷新文档,以秒计时。
  • Cookie
    可向服务端传递数据一种模型。
请求体

​ 客户端传递给服务器的数据。比如:一般用来存储post的参数和参数数据;

  1. Get 请求(没有请求体)

当请求方式是get时,浏览器会使用查询字符串的方式进行传递数据,即:query string Parameters
查询字符串的规则:请求参数使用 URL地址和“问号传参“ 的方式进行传递——用问号把url和请求参数分开。
请求参数的格式:以键值对的方式体现,多个键值对之间用&隔开。
假设:请求地址为 index.jsp。请求参数是uname和pwd。
那么,请求的格式为:index.jsp?uname=lili&pwd=123123
如下图,请求方式为get,在chrome浏览器的network里看到的是:query string Parameters

  1. Post 请求

那么请求的数据不是在url上,而是在请求体里。

HTTP协议之响应

​ 在接收和解析请求消息后,服务器返回一个 HTTP 响应消息。HTTP 响应也是由三个部分组成,分别是:响应行、响应头、响应体。

响应行

和请求消息相比,响应消息多了一个“响应状态码”,它以“清晰明确”的语言告诉客户端本次请求的处理结果。

HTTP状态码分类,共分为5种类型:

HTTP状态码列表

1xx

2xx

3xx

4xx

5xx

响应头

​ 响应头用于告知浏览器当前响应中的详细信息,浏览器通过获取响应头中的信息可以知道应该如何处理响应结果。响应头中信息的格式为key:value。

  • Date
    响应的Date使用的是GMT时间格式,表示响应消息送达时间。

  • Content-Encoding
    文档的编码(Encode)方式。用gzip压缩文档能够显著地减少HTML文档的响应时间。

  • Content-Length
    表示内容长度。只有当浏览器使用持久HTTP连接时才需要这个数据。

  • Content-Type
    表示响应的文档属于什么MIME类型。

  • MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型。是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。
    MIME作用:HTTP协议所产生的响应中正文部分可以是任意格式的数据,那么如何保证接收方能看得懂发送方发送的正文数据呢?HTTP协议采用MIME协议来规范正文的数据格式。
    在服务端我们可以设置响应头中Content-Type的值来指定响应类型。

  • MIME类型对应列表

响应体

​ 响应体就是响应的消息体,如果是纯数据就是返回纯数据,如果请求的是HTML页面,那么返回的就是HTML代码,如果是JS就是JS代码,如此之类。

Tomcat服务器

什么是Tomcat

​ Tomcat 是一个符合 JavaEE WEB 标准的最小的 WEB 容器,所有的 JSP 程序一定要有 WEB 容器的支持才能运行,而且在给定的 WEB 容器里面都会支持事务处理操作。

​ Tomcat 是由 Apache 提供的(www.apache.org)提供的可以用安装版和解压版,安装版可以在服务中出现一个 Tomcat 的服务,免安装没有,开发中使用免安装版。 Tomcat 简单的说就是一个运行 Java 的网络服务器,底层是 Socket 的一个程序,它也是 JSP 和 Servlet 的一个容器。 Tomcat 是 Apache 软件基金会(Apache Software Foundation)的 Jakarta 项目中的一个核心项目,由 Apache、Sun和其他一些公司及个人共同开发而成。

​ 由于有了 Sun 的参与和支持,最新的 Servlet 和 JSP 规范总是能在 Tomcat 中得到体现。因为 Tomcat 技术先进、性能稳定,而且免费,因而深受 Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的 Web 应用服务器

​ Tomcat 服务器是一个免费的开放源代码的 Web 应用服务器,属于轻量级应用服务器, 在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试 JSP 程序的首选。 对于一个初学者来说,可以这样认为,当在一台机器上配置好 Apache 服务器,可利用它响应 HTML(标准通用标记语言下的一个应用)页面的访问请求。实际上 Tomcat 部分是 Apache 服务器的扩展,但它是独立运行的,所以当你运行 tomcat 时,它实际上作为一个与 Apache 独立的进程单独运行的。

​ 当配置正确时,Apache 为 HTML 页面服务,而 Tomcat 实际上是在运行 JSP 页面和 Servlet。另外,Tomcat 和 IIS 等 Web 服务器一样,具有处理 HTML 页面的功能,另外它还是 一个 Servlet 和 JSP 容器,独立的 Servlet 容器是 Tomcat 的默认模式。不过,Tomcat 处理静态 HTML 的能力不如 Apache 服务器。目前 Tomcat 最新版本为 10.0。

安装Tomcat

tomcat是用java写的,所以使用需要jdk的。

下载地址

http://tomcat.apache.org/

Tomcat是一款绿色软件,我们下载压缩包,解压即可使用!

配置JAVA_HOME

​ 运行 Tomcat 需要 JDK 的支持【Tomcat 会通过 JAVA_HOME 找到所需要的 JDK】。 安装就是解压缩过程。启动 Tomcat,能访问则算安装好了

启动Tomcat

启动 Tomcat (在 tomcat 的安装目录下的 bin 目录 使用命令行启动 tomcat)

方式一:双击脚本文件启动(在D:\study_setup\apache-tomcat-9.0.70\bin中找到)

方式二:使用脚本命令启动

服务器启动成功:

注:

Tomcat默认占用端口8080。(注意端口冲突问题)

如果需要使用服务器,启动成功后,该启动窗口不要关闭。

打开浏览器,输入http://localhost:8080/ 访问

Tomcat目录结构

  • bin:用来存放Tomcat服务器的可执行程序,主要有两大类,一类是以.sh结尾的(linux命令),另一类是以.bat结尾的(windows命令)。比如:启动和关闭 tomcat 的 bat 文件

  • conf:用来存放Tomcat服务器的配置文件

    ​ server.xml可以设置端口号、设置域名或IP、默认加载的项目、请求编码、最大线程并发数等

    ​ web.xml可以设置tomcat支持的文件类型

    ​ context.xml可以用来配置数据源之类的

    ​ tomcat-users.xml用来配置管理tomcat的用户与权限

  • lib:主要用来存放tomcat运行需要加载的jar包。

  • logs:用来存放Tomcat服务器运行时输出的日志信息,在windows环境中,控制台的输出日志在catalina.xxxx-xx-xx.log文件中,比如记录你在什么时间启动服务器、关闭服务器,运行过程中报什么错等等,以后项目都是部署在远程服务器上而不是本地,那么你本地的控制台就没有意义了,此时想要查询日志,就要去服务器的日志文件中进行查询。

  • temp:用来存放Tomcat服务器运行时产生的临时数据

  • webapps:用来存放Tomcat服务器部署的工程,tomcat中自带一些项目。比如我们访问的http://localhost:8080/ 对应的页面,就是tomcat自带的ROOT项目。当然,如果你把你的项目放在webapps下,肯定也是可以的。

  • work:是Tomcat工作时的目录,用来存放Tomcat运行时jsp被访问后翻译为Servlet的源码和编译后的文件

常用配置:

(1)修改默认端口:

Tomcat默认监听端口为8080。可通过修改server.xml文件来改变Tomcat监听端口。

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

修改端口后,服务器要重启才可以生效。

在http协议下,如果你把端口号改为80,那么你访问服务器的时候80可以缺省不写了。(https协议下缺省端口号为443-https://www.baidu.com:443)

(2)解决控制台乱码:

控制台产生乱码的原因是在Tomcat在输出日志中使用的是UTF-8编码,而我们中文的Windows操作系统使用的是GBK编码。由于编码格式不统一,所以出现了乱码。

​ 解决方式:修改conf目录中的logging.properties属性文件重新指定的编码方式。如果还是不行,那么 就删除该行即可:

1
java.util.logging.ConsoleHandler.encoding = GBK

IDEA配置Tomcat

我们写好web项目,可以手动将项目放在tomcat的webapps目录下,然后手动点击startup.bat启动服务器,如果每次都是这样的操作,那么太麻烦了!所以我们需要将tomcat集成到idea中,提高开发效率。如何在idea中配置tomcat呢?

  1. 选择 “Appliction Servers”,点击右侧的 “+” 号,选择 “Tomcat Server”
  1. 设置 Tomcat 的安装目录

设置好之后

  1. 配置Tomcat服务器完成

Servlet的实现

​ Servlet 是 Server 与 Applet 的缩写,是服务端小程序的意思。使用 Java 语言编写的服务器端程序,可以生成动态的 WEB 页,Servlet 主要运行在服务器端,并由服务器调用执行, 是一种按照 Servlet 标准来开发的类。 是 SUN 公司提供的一门用于开发动态 Web 资源的技术。(言外之意:要实现 web 开发,需要实现 Servlet 标准)

​ eg:想要把数据库的内容展示在浏览器端,中间需要服务端程序,这个程序的编写,就需要通过servlet完成。

​ Servlet 本质上也是 Java 类,但要遵循 Servlet 规范进行编写,没有 main()方法,它的创建、使用、销毁都由 Servlet 容器进行管理(如 Tomcat)。(言外之意:写自己的类,不用写 main 方法,别人自动调用,每个servlet有一个地址,你可以在浏览器输入地址然后访问到。)

​ Servlet 是和 HTTP 协议是紧密联系的,其可以处理 HTTP 协议相关的所有内容。这也是 Servlet 应用广泛的原因之一。

​ 提供了 Servlet 功能的服务器,叫做 Servlet 容器,其常见容器有很多,如 Tomcat, Jetty, WebLogic Server, WebSphere, JBoss 等等。

​ Servlet是JavaWeb开发的三大组件之一(另外两个是过滤器filter与监听器listener)

创建Web项目

  1. 选择 “File” —> “New” —> “Project”,创建空项目,以便后续可以练习的内容都可以放在这个空项目下:

  2. 在空项目右键—>”new”—>”Module”:

    Servlet-18

  3. 创建第一个模块:ServletDemo01模块:

    Servlet-19

  4. 右键ServletDemo01模块-添加框架支持(英文版是Add Framework Support…)

    Servlet-20

  5. 勾选web应用:

    Servlet-21

  6. 创建模块成功:

    Servlet-22

web资源包括:引入的jar包、html、css、js、images等….

  1. 配置tomcat:

    添加配置:

    Servlet-23

    点击加号:

    Servlet-24

    选择本地tomcat:

    Servlet-25

    配置tomcat:

    Servlet-26

    在跳出来的页面点击应用和确定:

    Servlet-27

  2. 运行tomcat测试:

    点击启动服务器:

Servlet-28

​ 运行:

Servlet-29

​ 运行后自动帮我们打开页面:

Servlet-30

其实帮我们默认打开的就是:http://localhost:8888/ServletDemo01_war_exploded/index.jsp页面

Servlet的实现

新建类

  1. 点击 “src” —> “new” —> “package”,创建一个文件包

Servlet-31

​ 录入包名:

Servlet-32

  1. 在包下面创建 Java 类文件,点击包名 —> “New” —> “Java Class”

Servlet-33

​ 录入类名:

Servlet-34

  1. 创建一个普通的 Java 类

Servlet-35

​ 如下代码:

1
2
3
4
5
6
7
8
package com.msb.testservlet;
/**
* @Author: zhaoss
*/
public class Servlet01{

}

导入jar包

在”web” —> “WEB_INF”下创建文件夹:

Servlet-36

文件夹名字为lib:

Servlet-37

完成后为如下目录:

Servlet-38

找到tomcat文件夹下的lib目录下的servlet-api.jar包粘贴到lib目录下,然后右键add as library…

Servlet-39

实现 Servlet 规范

​ 该类必须实现 Servlet 规范,即继承 HttpServlet 类(这个类需要jar包支持,这个jar包在哪里?在tomcatl-ib下的servlet-api.jar中),后续只需要进行业务的实现即可。

1
2
3
4
5
6
7
8
9
package com.msb.testservlet;

/**
* @Author: zhaoss
*/
public class Servlet01 extends HttpServlet {

}

重写 service 方法

​ 满足 Servlet 规范只是让我们的类能够满足接收请求的要求,接收到请求后需要对请求进行分析,以及进行业务逻辑处理,计算出结果,则需要添加代码,在规范中有一个叫做 service的方法,专门用来做请求处理的操作,我们要重写service方法,业务代码则可以写在该方法中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.msb.testservlet;

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

/**
* @Author: zhaoss
*/
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 控制台打印一句话
System.out.println("hi servlet");
// 响应一句话给浏览器
resp.getWriter().write("hi servlet");
}
}

设置注解

​ 在完成好了一切代码的编写后,还需要向服务器说明,特定请求对应特定资源。

​ 开发servlet项目,使用@WebServlet将一个继承于javax.servlet.http.HttpServlet 的类定义为Servlet组件。在Servlet3.0中 , 可以使用@WebServlet注解将一个继承于javax.servlet.http.HttpServlet的类标注为可以处理用户请求的 Servlet。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.msb.testservlet;

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

/**
* @Author: zhaoss
*/
@WebServlet("/s01")
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 控制台打印一句话
System.out.println("hi servlet");
// 响应一句话给浏览器
resp.getWriter().write("hi servlet");
}
}

还可以用其它写法配置 Servlet:

1
@WebServlet(name="Servlet01",value="/ser01")
1
@WebServlet(name="Servlet01",urlPatterns = "/ser01")

也可以配置多个访问路径

1
@WebServlet(name="Servlet01",value={"/ser01",'/ser001'})
1
@WebServlet(name="Servlet01",urlPatterns={"/ser01",'/ser001'})

发布项目并启动服务

​ 到此,需要编写的地方已经完成,项目已经完整了,但是如果需要外界能够访问, 还需要将项目发布到服务器上并运行服务器。

  1. 设置项目的站点名(项目对外访问路径)

  1. 启动服务器

访问并查看结果

​ 在项目正确发布到服务器上之后,用户即可通过浏览器访问该项目中的资源。注意 url 的 格式正确,tomcat 的端口为 8888。

​ 浏览器访问地址:http://localhost:8888/s/s01

页面效果

后台结果

到这里我们的第一个 Servlet 就实现了!

Servlet的工作流程

我们通过浏览器f12可以清晰的看到整个请求过程:

  1. 通过请求头获知浏览器访问的是哪个主机

  2. 再通过请求行获取访问的是哪个一个web应用

  3. 再通过请求行中的请求路径获知访问的是哪个资源

  4. 通过获取的资源路径在配置中匹配到真实的路径,

  5. 服务器会创建servlet对象,(如果是第一次访问时,创建servlet实例,并调用init方法进行初始化操作)

  6. 调用service(request, response),并会生成HttpServletRequest实现类对象和HttpServletResponse实现类对象,用来处理请求和响应

  7. 调用service完毕后返回服务器 由服务器将response缓冲区的数据取出,以http响应的格式发送给浏览器

Servlet的其它实现方式

方式1:extends HttpServlet 类

方式2:extends GenericServlet抽象类

方式3:implements Servlet接口

用的最多:方式1

方式1代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.msb.testservlet;

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

/**
* @Author: zhaoss
*/
@WebServlet("/s01")
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 控制台打印一句话
System.out.println("hi servlet");
// 响应一句话给浏览器
resp.getWriter().write("hi servlet");
}

}

方式2代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.msb.testservlet;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import java.io.IOException;

/**
* @Author: zhaoss
*/
@WebServlet("/s02")
public class Servlet02 extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("servlet02...");
}
}

方式3代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.msb.testservlet;

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

/**
* @Author: zhaoss
*/
@WebServlet("/s03")
public class Servlet03 implements Servlet {

@Override
public void init(ServletConfig servletConfig) throws ServletException {

}

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

@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("servlet03...");
}

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

@Override
public void destroy() {

}
}

启动服务器,依次访问Servlet测试即可。

http://localhost:8888/s/s01

http://localhost:8888/s/s02

http://localhost:8888/s/s03

Servlet中方法的其它实现方式:

父类HttpServlet的service方法中会对请求做分发处理:

所以子类 Servlet01中可以不用重写service方法,直接写doGet和doPost方法,被父类的service方法调用即可:(不推荐)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.msb.testservlet;

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

/**
* @Author: zhaoss
*/
@WebServlet("/s01")
public class Servlet01 extends HttpServlet {
//@Override
/*protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 控制台打印一句话
System.out.println("hi servlet");
// 响应一句话给浏览器
resp.getWriter().write("hi servlet");
}*/

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

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//System.out.println("post....");
this.doGet(req,resp);//无论get请求和post请求都会走同一套业务逻辑。
}
}

Servlet的对外访问路径的设置:

启动web项目后,servlet容器会读取web.xml配置文件,我们可以在web.xml中对servlet路径做配置:

web.xml中代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>a</servlet-name>
<servlet-class>com.msb.testservlet.Servlet04</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>a</servlet-name>
<url-pattern>/s04</url-pattern>
</servlet-mapping>
</web-app>

Servlet代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.msb.testservlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
* @Author: zhaoss
*/
public class Servlet04 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servlet04");
}
}

Servlet的生命周期

​ Servlet没有 main()方法,不能独立运行,它的运行完全由 Servlet 引擎来控制和调度。 所谓生命周期,指的是 servlet 容器何时创建 servlet 实例、何时调用其方法进行请求的处理、 何时并销毁其实例的整个过程。

  • 实例和初始化时机

    当请求到达容器时,容器查找该 servlet 对象是否存在,如果不存在,则会创建实例(通过反射)并进行初始化。

  • 就绪/调用/服务阶段

    有请求到达容器,容器调用 servlet 对象的 service()方法,处理请求的方法在整个生命周期中可以被多次调用; HttpServlet 的 service()方法,会依据请求方式来调用 doGet()或者 doPost()方法。但是, 这两个 do 方法默认情况下,会抛出异常,需要子类去 override。

  • 销毁时机

    当容器关闭时(应用程序停止时),会将程序中的 Servlet 实例进行销毁。

    上述的生命周期可以通过 Servlet 中的生命周期方法来观察。在 Servlet 中有三个生命周 期方法,不由用户手动调用,而是在特定的时机有容器自动调用,观察这三个生命周期方法 即可观察到 Servlet 的生命周期。

测试:定义一个Servlet,继承HttpServlet后重写如下三个方法:

init 初始化方法,在 Servlet 实例创建之后执行(证明该 Servlet 有实例创建了),系统方法服务器自动调用。

1
2
3
public void init(ServletConfig config) throws ServletException {
System.out.println("实例创建了...");
}

service 就绪/调用/服务方法,每次有请求到达某个 Servlet 方法时执行,用来处理请求(证明该Servlet 进行服务了),系统方法服务器自动调用。

1
2
3
4
protected void service(HttpServletRequest req, HttpServletResponse resp) 
throws ServletException, IOException {
System.out.println("服务调用了...");
}

destroy 销毁方法,Servlet 实例销毁时执行(证明该 Servlet 的实例被销毁了),系统方法服务器自动调用。

1
2
3
public void destroy() {
System.out.println("实例销毁了...");
}

除了上述三个方法外,再加入Servlet的构造方法,即可看到创建对象时机:

1
2
3
public Servlet04() {
System.out.println("构造方法调用...");
}

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.msb.testservlet;

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

/**
* @Author: zhaoss
*/
@WebServlet("/s05")
public class Servlet05 extends HttpServlet {
public Servlet05(){
System.out.println("构造器被调用了");
}

@Override
public void init() throws ServletException {
System.out.println("Servlet-init");
}

@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("处理业务 ");
}

@Override
public void destroy() {
System.out.println("Servlet对象被销毁");
}
}

执行结果:

Servlet 的生命周期:

​ 1. Web Client 向 Servlet 容器(Tomcat)发出 Http 请求

​ 2. Servlet 容器接收 Web Client 的请求

​ 3. 根据反射构建Servlet对象(第一次请求会构建,之后不会重复创建对象,直到服务器关闭才会被销毁)

  4. 会构建HttpServletRequest 实现类对象、HttpServletResponse 实现类对象 ,处理请求和响应
  1. Servlet 容器调Servlet 对象的service 方法,把HttpServletRequest 实现类对象 与 HttpServletResponse 实现类对象 作为service的参数,供给方法内部使用

    1. Servlet 对象调用 HttpServletRequest 实现类对象的有关方法,获取 Http 请求信息

    2. Servlet 对象调用 HttpServletResponse实现类 对象的有关方法,生成响应数据

    3. Servlet 容器把 Servlet对象 的响应结果传给 Web Client

HttpServletRequest对象

​ HttpServletRequest 对象:主要作用是用来接收客户端发送过来的请求信息,例如:请求的参数,发送的头信息等都属于客户端发来的信息,service()方法中形参接收的是 HttpServletRequest 接口的实例化对象,表示该对象主要应用在 HTTP 协议上,该对象是由 Tomcat 封装好传递过来。一切请求都围绕HttpServletRequest 对象。

​ HttpServletRequest 是 ServletRequest 的子接口,ServletRequest 只有一个子接口,就是 HttpServletRequest。既然只有一个子接口为什么不将两个接口合并为一个?

​ 从长远上讲:现在主要用的协议是 HTTP 协议,但以后可能出现更多新的协议。若以后想要支持这种新协议,只需要直接继承 ServletRequest 接口就行了。

​ 在 HttpServletRequest 接口中,定义的方法很多,但都是围绕接收客户端参数的。但是怎么拿到该对象呢?不需要,直接在 Service 方法中由容器传入过来,而我们需要做的就是取出对象中的数据,进行分析、处理。

接收请求

常用方法

  1. 方法

  2. 示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 获取客户端请求的完整URL (从http开始,到?前面结束)
    String url = request.getRequestURL().toString();
    System.out.println("获取客户端请求的完整URL:" + url);
    // 获取客户端请求的部分URL (从站点名开始,到?前面结束)
    String uri = request.getRequestURI();
    System.out.println("获取客户端请求的部分URL:" + uri);
    // 获取请求行中的参数部分
    String queryString = request.getQueryString();
    System.out.println("获取请求行中的参数部分:" + queryString);
    // 获取客户端的请求方式
    String method = request.getMethod();
    System.out.println("获取客户端的请求方式:" + method);
    // 获取HTTP版本号
    String protocol = request.getProtocol();
    System.out.println("获取HTTP版本号:" + protocol);
    // 获取webapp名字 (站点名)
    String webapp = request.getContextPath();
    System.out.println("获取webapp名字:" + webapp);

结果,如果客户端未传递参数http://localhost:8888/s/sr01,结果为:

如果传递了参数http://localhost:8888/s/sr01?uname=lili&pwd=123123,结果为:

获取请求参数

  1. 方法

  2. 示例

    1
    2
    3
    4
    5
    6
    // 获取指定名称的参数,返回字符串
    String uname = request.getParameter("uname");
    System.out.println("uname的参数值:" + uname);
    // 获取指定名称参数的所有参数值,返回数组
    String[] hobbys = request.getParameterValues("hobby");
    System.out.println("获取指定名称参数的所有参数值:" + Arrays.toString(hobbys));

访问http://localhost:8888/s/sr01?uname=lili&pwd=123123&hobby=sing&hobby=dance,结果为:

请求乱码问题

案例:

(1)先将服务器改为tomcat7

(2)编写login.jsp和servlet,从login.jsp请求到servlet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.msb.testrequest;

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

/**
* @Author: zhaoss
*/
@WebServlet("/sr02")
public class ServletRequest02 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String uname = req.getParameter("uname");
System.out.println("接收到的uname参数为:" + uname);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<%--
Created by IntelliJ IDEA.
User: zss
Date: 2022/12/29
Time: 12:56
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>
<form action="sr02" method="get">
用户名:<input type="text" name="uname">
<input type="submit" value="提交">
</form>
</body>
</html>

注意:(1)JSP虽然还没有学习,但是感觉可以写入html代码。(2)action中servlet地址,不用加/,直接写sr02即可。因为src目录和web目录都属于根目录,可以理解为平级的,所以从jsp中请求servlet不需要加/。

分别修改method为post和get,请求:http://localhost:8888/ServletDemo01_war_exploded/login.jsp:

点击提交后,后台结果:

发现问题:无论get方式还是post方式提交,后台均会出现乱码。出现乱码的原因:

如何解决呢?

1
new String(request.getParameter(name).getBytes("ISO-8859-1"),"UTF-8");

request.getParameter(name).getBytes(“ISO-8859-1”) ——-> 先将字符串用ISO-8859-1码转为字节

new String(request.getParameter(name).getBytes(“ISO-8859-1”),”UTF-8”); ——-> 然后将该字节以UTF-8转成字符

测试,无论get方式和post方式都不会出现乱码了:

但是上面方式有缺点:

假如表单中有uname,password,hobby等等多个元素,那么在后台解决的话每个都要这样转一下 ,太麻烦了!那有没有更快速的方式呢?

上面的问题就是出在传过来的字节转字符串默认的解码方式为ISO-8859-1,那么我们直接将这个码改为UTF-8就可以了:

1
request.setCharacterEncoding("UTF-8");

再次测试:

发现get方式依然乱码,但是post方式乱码问题得到了解决。

原因:这种方式只针对 POST 有效(必须在接收所有的数据之前设定)

所以我们怎么办呢?将tomcat使用8以上版本,Tomcat8起,GET方式请求是不会出现乱码的。然后再配合request.setCharacterEncoding(“UTF-8”); ,那么get方式和post方式的乱码问题就都得到了解决。

请求转发

​ 请求转发,是一种服务器的行为,当客户端请求到达后,服务器进行转发,此时会将请求对象进行保存,地址栏中的 URL 地址不会改变,得到响应后,服务器端再将响应发送给客户端,从始至终只有一个请求发出

实现方式如下,达到多个资源协同响应的效果。

1
request.getRequestDispatcher(url).forward(request,response);

案例:

(1)编写一个页面login.jsp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<%--
Created by IntelliJ IDEA.
User: zss
Date: 2022/12/29
Time: 13:43
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>
<form action="sr03" method="post">
用户名:<input type="text" name="uname">
<input type="submit" value="登录">
</form>
</body>
</html>

(2)因为请求转发是一种服务器的行为,当客户端请求到达后,服务器进行转发,所以定义两个Servlet,从第一个Servlet请求转发到第二个Servlet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.msb.testrequest;

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

/**
* @Author: zhaoss
*/
@WebServlet("/sr03")
public class ServletRequest03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取前台的表单传递过来的数据:
String uname = req.getParameter("uname");
System.out.println("浏览器传过来的数据uname的值:" + uname);
// 请求转发到另一个Servlet:
req.getRequestDispatcher("sr04").forward(req,resp);
// 请求转发后输出一句话:
System.out.println("------sr03");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.msb.testrequest;

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

/**
* @Author: zhaoss
*/
@WebServlet("/sr04")
public class ServletRequest04 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取上一个Servlet传过来的数据:
String uname = req.getParameter("uname");
System.out.println("上一个Servlet传过来的数据uname的值:" + uname);
}
}

启动服务器,浏览器发送请求:http://localhost:8888/s/login.jsp

后台结果:

前台浏览器地址栏:

原理:

除了可以请求转发到Servlet,还可以请求转发到jsp中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.msb.testrequest;

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

/**
* @Author: zhaoss
*/
@WebServlet("/sr03")
public class ServletRequest03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取前台的表单传递过来的数据:
String uname = req.getParameter("uname");
System.out.println("浏览器传过来的数据uname的值:" + uname);
// 请求转发到另一个Servlet:
req.getRequestDispatcher("index.jsp").forward(req,resp);
// 请求转发后输出一句话:
System.out.println("------sr03");
}
}

地址栏依旧是请求的第一个Servlet:

是否可以请求转发到服务器外部资源?比如百度首页:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.msb.testrequest;

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

/**
* @Author: zhaoss
*/
@WebServlet("/sr03")
public class ServletRequest03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取前台的表单传递过来的数据:
String uname = req.getParameter("uname");
System.out.println("浏览器传过来的数据uname的值:" + uname);
// 请求转发到另一个Servlet:
//req.getRequestDispatcher("sr04").forward(req,resp);
//req.getRequestDispatcher("index.jsp").forward(req,resp);
req.getRequestDispatcher("http://www.baidu.com").forward(req,resp);
// 请求转发后输出一句话:
System.out.println("------sr03");
}
}

结果:

错误描述信息 :/s/https://www.baidu.com
因为是服务器跳转,会去当前的项目下找,但是找不到,报404错误,所以没法完成向服务器外部资源请求转发。

HttpServletRequest对象的使用

​ HttpServletRequest可以作为一个域对象(request域对象)使用,通过该对象可以在一个请求中传递数据,作用范围:在一次请求中有效,即服务器跳转有效。

1
2
3
4
5
6
// 设置域对象内容
request.setAttribute(String name, String value);
// 获取域对象内容
request.getAttribute(String name);
// 删除域对象内容
request.removeAttribute(String name);

​ request 域对象中的数据在一次请求中有效,则经过请求转发,request 域中的数据依然存在,则在请求转发的过程中可以通过 request 来传输/共享数据。

案例:请求转发后request 域对象中的数据可以共享。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.msb.testrequest;

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

/**
* @Author: zhaoss
*/
@WebServlet("/msb/sr03")
public class ServletRequest03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取前台传过来的数据:
String uname = req.getParameter("uname");
System.out.println("前台传到sr03-Servlet中的uname的数值为:" + uname);
// 域对象数据的共享:
req.setAttribute("age","18");
String age = (String)req.getAttribute("age");
System.out.println("servlet03---age:" + age);
// 请求转发:
req.getRequestDispatcher("/sr04").forward(req,resp);
// 在请求转发以后输出一句话:
System.out.println("-----sr03");
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.msb.testrequest;

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

/**
* @Author: zhaoss
*/
@WebServlet("/sr04")
public class ServletRequest04 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取请求数据:
String uname = req.getParameter("uname");
// 获取域对象中数据:
String age = (String)req.getAttribute("age");
System.out.println("servlet04---age:" + age);
System.out.println("sr04-Servlet中获取uname的数值为:" + uname);
}
}

HttpServletResponse对象

​ Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的 request 对象和代表响应的 response 对象。

​ request 和 response 对象代表请求和响应:获取客户端数据,需要通过 request 对象;向客户端输出数据,需要通过 response 对象

​ HttpServletResponse 的主要功能用于服务器对客户端的请求进行响应,将 Web 服务器处理后的结果返回给客户端。service()方法中形参接收的是 HttpServletResponse 接口的实例化对象,这个对象中封装了向客户端发送数据、发送响应头,发送响应状态码的方法。

响应数据

​ 接收到客户端请求后,可以通过 HttpServletResponse 对象直接进行响应,响应时需要获取输出流。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 响应数据:
// PrintWriter字符输出流-获取字符流(只能响应回字符)
resp.getWriter().write("hi01");//只能打印输出文本格式,不可以输出对象
resp.getWriter().print("hi01");//可以输出对象

// ServletOutputStream字节输出流-获取字节流(能响应一切数据)
resp.getOutputStream().write("hi".getBytes());
// 设置响应头,一般用于更新原来的响应头,key相同,后面设置的会覆盖前面设置的
resp.setHeader("key1","value1");
resp.setHeader("key1","value2");
// 设置响应头,一般用于添加新的响应头,key不同,后面设置的不会覆盖前面设置的
resp.addHeader("key3","value3");
resp.addHeader("key3","value4");

结果:

响应乱码问题

​ 在响应中,如果我们响应的内容中含有中文,则有可能出现乱码。这是因为服务器响应的数据也会经过网络传输,服务器端有一种编码方式,在客户端也存在一种编码方式,当两端使用的编码方式不同时则出现乱码。

案例:

(1)请求一个servlet,向页面响应中文字符串,发现出现乱码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.msb.testresponse;

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

/**
* @Author: zhaoss
*/
@WebServlet("/msb/sr02")
public class ServletResponse02 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().print("你好");
}
}

启动服务器请求Servlet,访问http://localhost:8888/s/msb/sr02

结果:

(2)出现乱码的原因:

设置传输过程中编码格式:

1
response.setCharacterEncoding("UTF-8");

上述代码,写在响应之前。

设置浏览器解码格式:

1
2
3
// 下面两行代码是等价的
response.setHeader("content-type", "text/html;charset=UTF-8");
response.setContentType("text/html;charset=UTF-8");

总结:

以后要想请求和响应都没有乱码问题,写Servlet上来加入三句代码:

1
2
3
4
5
6
// 设置请求信息的解码格式:
req.setCharacterEncoding("UTF-8");
// 设置响应信息的编码格式:
resp.setCharacterEncoding("UTF-8");
// 设置浏览器的编码格式:
resp.setContentType("text/html;charset=UTF-8");

重定向

​ 重定向是一种服务器指导,客户端的行为。客户端发出第一个请求,被服务器接收处理后,服务器会进行响应,在响应的同时,服务器会给客户端一个新的地址(下次请求的地址 response.sendRedirect(url);),当客户端接收到响应后,会立刻、马上、自动根据服务器给的新地址发起第二个请求,服务器接收请求并作出响应,重定向完成。

​ 从描述中可以看出重定向当中有两个请求存在,并且属于客户端行为。

1
2
// 重定向跳转到index.jsp
response.sendRedirect("index.jsp");

案例:

(1)定义两个Servlet,从第一个Servlet重定向到第二个Servlet:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.msb.testresponse;

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

/**
* @Author: zhaoss
*/
@WebServlet("/red1")
public class ServletResponse03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取前台传过来的参数:
String uname = req.getParameter("uname");
System.out.println("前台传递过来的参数:" + uname);
// 重定向:
resp.sendRedirect("red2");
// 重定向后打印一句话:
System.out.println("---重定向后打印内容----");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.msb.testresponse;

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

/**
* @Author: zhaoss
*/
@WebServlet("/red2")
public class ServletResponse04 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 看看是否能获取重定向过来的数据:
String uname = req.getParameter("uname");
System.out.println("是否可以获取到重定向过来的数据呢?--》" + uname);
}
}

(2)访问第一个Servlet,http://localhost:8888/s/red1?uname=lili,查看结果:

前台:

后台:

浏览器f12:通过观察浏览器我们发现第一次请求获得的响应码为 302,并且含有一个 location 头信息。并且地址栏最终看到的地址是和第一次请求地址不同的,地址栏已经发生了变化。

原理:

那么我们可以请求服务器外部资源吗?试试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.msb.testresponse;

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

/**
* @Author: zhaoss
*/
@WebServlet("/red1")
public class ServletResponse03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取前台传过来的参数:
String uname = req.getParameter("uname");
System.out.println("前台传递过来的参数:" + uname);
// 重定向:
resp.sendRedirect("http://www.baidu.com");
// 重定向后打印一句话:
System.out.println("---重定向后打印内容----");
}
}

重定向后地址栏:

请求转发与重定向的区别

不同1:语法不同

请求转发:request.getRequestDispatcher(“dis02”).forword(request,response);
重定向:response.sentRedirect(“red02”);

不同2:请求不同
请求转发:是一个请求
重定向:是不同的请求

不同3:地址栏是否发生改变

请求转发:不变
重定向:变

不同4:请求参数能否携带

请求转发:可以
重定向:不可以, 但是可以手动设置携带参数:resp.sendRedirect(“red2?uname=”+req.getParameter(“uname”)+”&pwd=123”);

不同5:执行效率不同

请求转发:服务器内部跳转,所以效率高啊
重定向:相对效率低

不同6:跳转范围不同

请求转发:只能在服务器内部跳转
重定向:既能跳转服务器内部资源,又能跳转服务器外部资源

不同7:表单是否发生重复提交

请求转发:会,指的是数据提交后,刷新页面,因为地址栏地址不变,每刷新一次都会重复请求一次,数据提交一次。如果刚好是个添加操作,每次都会添加一次。

重定向:不会

不同8:路径书写方式不同

(1)绝对路径 - 有协议、IP、端口 ,eg: http://www.baidu.com

​ 请求转发:不支持

​ 重定向:支持

(2)相对路径:相对servlet的url地址的路径 (不建议)

​ 自行测试:将第一个servlet地址改为”/msb/s1”,然后分别请求转发,重定向到第二个servlet,观看相对地址和运行结果

​ 对于不加/,就是相对路径,相对servlet配置的url-pattern(请求地址),所以你要是url-pattern配置的是 /a 那么不加/ 没啥问题 ,但是url-pattern里面要是 /a/b/c 那么在访问jsp的时候 可能就出错了! 代码不加/就出错了!

(3)根路径: 资源路径加”/“ (建议)

​ 请求转发:到项目根目录下查找资源 http://localhost:8080/项目访问名/

​ 重定向:到服务器根目录下查找资源 http://localhost:8080/

​ eg:resp.sendRedirect(req.getContextPath()+”/dis2”);

转发和重定向使用场景

1、跳转前后是否需要共享request中数据

​ 如果需要共享:转发

​ 如果不需要共享:都可以

2、是否要跳转到服务器外部资源

​ 如果需要跳出项目:重定向

   如果不需要:都可以

3、是否涉及表单重复提交

​ 重定向:不会造成表单重复提交

​ 转发:会造成表单重复提交