【Java企业级开发】(十一)企业级Web应用程序Servlet框架的使用(上)

一、Servlet入门

1.1 内容概述

  • 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。

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

**客户端:**浏览器

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

**请求:**客户端发送数据给服务端的过程

**响应:**服务端返回数据给客户端的过程

**HTTP协议:**客户端请求和响应的标准协议

1.3 HTTP协议

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

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

1.3.1 浏览器中的书写格式

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

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

1.3.2 HTTP协议的特点

(1)支持客户/服务器模式

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

② 请求遵照http协议

③ 响应遵照httpp协议

④ 客户端、服务器关闭

(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再带一份水,而不是说:给我带一份饭再加一瓶水,如果每次都需要处理前面的信息,那么信息重传会导致传送的数据量增大,不需要处理之前的逻辑响应肯定也会更快。

1.3.3 HTTP的URL

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

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

http://host[:port]/[abs_path]

http://IP(主机名/域名):端口/访问的资源路径

  • http表示要通过HTTP协议来定位网络资源;
  • host表示合法的Internet主机域名或者IP地址;eg:本机地址可以用localhost或者127.0.0.1
  • port指定一个端口号,为空这使用缺省端口80,eg:tomcat默认端口是8080
  • abs_path指定请求资源的URL;如果URL中没有给出abs_path,那么当他作为请求URL时,必须以"/"的形式给出,通常这个工作浏览器会自动帮我们完成
  • 请求参数利用?拼接,多个参数用&相连
1.3.4 HTTP协议的请求
(1)浏览器向服务器发送请求种类
  1. 浏览器输入URL地址
  2. 表单提交
  3. 超链接
(2)HTTP请求由三部分组成

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

请求行(现在很多网站已经不显示请求行了)

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

**URI(统一资源标识符)**是用于标识或定位互联网资源的字符串,包含两种形式:统一资源定位符(URL)和统一资源名称(URN)。URL通过地址定位资源,而URN通过名称空间中的唯一名称标识资源。URI的标准格式包括协议名、服务器地址、路径、查询参数和片段ID等要素。URI在网络资源定位和标识中起着重要作用,通常用作HTTP请求的目标。需要注意的是,URI并不是URL的子集,而是一个更广泛的概念,URL是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加密协议
  • Catcge-Control
    • 指定请求和响应遵循的缓存机制
  • User-Agent
    • 浏览器表明自己的身份(是哪种浏览器)。例如Chrome浏览器:Mozilla/5.0(Windows NT 10.0;Win64;64)AppleWebKit/537.36(HKTMK.like Gecko) Chrom/81.0.4044.129 Safari/537.36.
  • Accept
    • 告诉WEB服务器自己接收什么介质类型,/表示任何类型,type/*白哦是该类型下是的所有子类型。
  • Accept-Encoding
    • 浏览器申明自己接收的编码方法,通常指定压缩方法,是否支持压缩,支持什么压缩方法(gzip,deflate)。
  • Accept-Charset
    • 浏览器告诉服务器自己能接收的字符集。
  • Accept-Language
    • 浏览器申明自己接收的语言。语言跟字符集的区别:中文是语言,中文有多种字符集,比如big5,gb2312,gbk等。
  • Rederer
    • 浏览器向WEB服务器表明自己是从哪个网页URL获取的点击当前请求中的网址/URL。
  • Refresh
    • 表示浏览器应该在多少时间之后刷新文档,以秒计时。
  • Cookie
    • 可以向服务端传递数据的一种模型
请求体(body)

get

请求方式是get时,浏览器会使用查询字符串的方式进行传递数据,即:query string Parameters

查询字符串的规则:请求参数使用URL地址和"问号传参"的方式进行传递---用问好把url和请求参数分开。

请求参数的格式:以键值对的方式体现,多个键值对之间用&隔开。

示例:https://xxx.com/action?url=124\&pwd=456

post

请求方式是post时,请求参数不是在url上,而是在请求体里面

1.3.5 HTTP协议的响应

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

(1)响应行

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

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

分类 分类描述
1xx 信息,服务器接收到请求,需要请求者继续执行操作
2xx 成功,操作被成功接收并处理
3xx 重定向,休要进一步的操作以完成请求
4xx 客户端错误,请求包含语法错误或者无法完成请求
5xx 服务器错误,服务器在处理请求的过程中发生了错误

HTTP状态码列表

状态码 状态码英文名称 中文描述
100 Continue 继续。客户端应继续其请求
101 Switching Protocols 切换协议。服务器根据客户端的请求切换协议。只能切换到更高的协议,例如,切换到HTTP的新版本协议
[1xx]
状态码 状态码英文名称 中文描述
200 OK 请求成功。一般用于GET与POST请求
201 Created 已创建。成功请求并创建了新的资源
202 Accepted 已接受。已经接收到请求,但是未完成处理
203 Non-Authoritative Information 非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本
204 No Content 无内容。服务器成功处理,但是未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档
205 Reset Content 重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清楚浏览器的表单域
206 Partial Content 部分内容。服务器成功处理了部分GET请求
[2xx]

| 状态码 | 状态码英文名称 | 中文描述 |
| 300 | Multiple Choices | 多种选择。请求的资源可包括多个位置,相应返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择 |
| 301 | Moved Permanently | 永久移动。请求的资源已被永久的移动到新的URI,返回信息会包括新的URI,浏览器会自动定向到URI。今后任何新的请求都应使用新的URI代替 |
| 302 | Found | 临时移动。与301类似,但资源只是临时被移动。客户端应继续使用原有URI |
| 303 | See Other | 查看其他地址。与301类似。使用GET和POST请求查看 |
| 304 | Not Modified | 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源 |
| 305 | Use Proxy | 使用代理。所请求的资源必须通过代理访问 |
| 306 | Unused | 已经被废弃的HTTP状态码 |

307 Temporary Redirect 临时重定向。与302类似。使用GET请求重定向
[3xx]

| 状态码 | 状态码英文名称 | 中文描述 |
| 400 | Bad Request | 客户端请求的语法错误(请求参数错误),服务器无法理解 |
| 401 | Unauthorized | 请求要求用户的身份认证(无权限) |
| 402 | Payment Required | 保留,将来使用 |
| 403 | Forbidden | 服务器理解请求客户端的请求,但是拒绝执行此请求 |
| 404 | Not Found | 服务器无法根据客户端的请求找到资源(网页)。通过此代码。网站设计人员可设置"您所请求的资源无法找到"的个性页面 |
| 405 | Method Not Allowed | 客户端请求中的方法被禁止 |
| 406 | Not Acceptable | 服务器无法根据客户端请求的内容特性完成请求 |
| 407 | Proxy Authentication Request | 请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权 |
| 408 | Request Time-out | 服务器等待用户发送的请求时间过长,超时 |
| 409 | Conflict | 服务器完成客户端的PUT请求时可能返回此代码,服务器处理请求时发生了冲突 |
| 410 | Gone | 客户端请求的资源已经不存在。410不同于404,如果资源以前有,现在被删除了可以使用410代码,网站设计人员可通过301代码指定资源的新位置 |
| 411 | Length Request | 服务器无法处理客户端发送的不带Content-Length的请求信息 |
| 412 | Precondition Failed | 客户端请求信息的先决条件错误 |
| 413 | Request Entity Too Large | 由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个Retry-After的相应信息 |
| 414 | Request-URI TOO Large | 请求的URI过长(URI通常为网址),服务器无法处理 |
| 415 | Unsupported Media Type | 服务器无法处理请求附带的媒体格式 |
| 416 | Requested range not satisfiable | 客户端请求的范围无效 |

417 Expectation Failed 服务器无法满足Expect的请求头信息
[4xx]

| 状态码 | 状态码英文名称 | 中文描述 |
| 500 | Internal Server Error | 服务器内部错误,无法完成请求 |
| 501 | Not Implemented | 服务器不支持请求的功能,无法完成请求 |
| 502 | Bad Gateway | 作为网关或者代理工作的服务器尝试执行请求时,从远处服务器接收到了一个无效的相应 |
| 503 | Service Unavailable | 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中 |
| 504 | Gateway Time-out | 充当网关或代理的服务器,未及时从远端服务器获取请求 |

505 HTTP Version not supported 服务器不支持的HTTP协议的版本,无法完成处理
[5xx]
(2)响应头

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

  • Date
    • 响应的Date使用的是GMT时间格式,表示响应消息送达时间
  • Content-Encoding
    • 文档的编码(Encode)方式。用gzip压缩文档能够显著地减少HTML文档的响应时间
  • Content-Length
    • 表示内容长度。只有当浏览器使用持久HTTP连接时才需要这个数据
  • Content-Type
    • 表示响应的文档属于什么MIME类型
(3)响应体

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

二、Servlet的实现

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

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

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

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

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

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

2.1 添加Tomcat配置

2.2 Servlet的实现

2.2.1 新建类

(1)点击"src"--->"new"--->"package",创建一个包

这里我们创建的Servlet01只是一个普通的类,实际上是不具备Servlet功能的,如果想实现Servlet的功能,我们必须得遵照Servlert的规范,那么我们就需要继承一个HttpServlet接口

但是这样的话,我们就得导入HttpServlet的这个依赖了,而这个依赖则是来自于我们的Tomcat安装包的下的"/lib/servlet-api.jar"这个包,把这个包放到"/web/WEB-INF/lib"下即可

2.2.2 导入jar包

将jar包添加为库

2.2.3 重写servlet方法
2.2.4 最终代码
java 复制代码
package com.study.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;

@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.3 Servlet的工作流程

  1. 通过请求头获知浏览器访问的是哪个主机
  2. 再通过请求行获取访问的是哪一个web应用
  3. 在通过请求行中的请求路径获知访问的是那个资源
  4. 通过获取的资源路径在配置中匹配到真实的路径
  5. 服务器会创建servelt对象(如果是第一次访问时,创建servlet示例,并调用init方法进行初始化操作)
  6. 调用servlet(request、response),并会生成HttpServletRequest对象和HttpServletResponse对象,用来处理请求和响应
  7. 调用servlet完毕后返回服务器,由服务器将response缓冲区的数据取出,以http响应的格式发送给浏览器

2.4 Servlet的其他实现方式

方式一:extends HttpServlet类
java 复制代码
package com.study.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;

@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");
    }
}
方式二:extends GenericServlet抽象类
java 复制代码
package com.study.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;

@WebServlet("/s02")
public class Servlet02 extends GenericServlet {
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("Servlet的GenericServlet实现方式.....");
    }
}
方式三:implements Servlet接口
java 复制代码
package com.study.testservlet;

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

@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("implements Servlet 实现....");
    }

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

    @Override
    public void destroy() {

    }
}

2.5 Servlet的生命周期

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

  • 实例和初始化时机
    • 当请求到达容器时,容器查找该servlet对象是否存在,如果不存在,则会创建实例(通过反射)并进行初始化;
  • 就绪/调用/服务阶段
    • 有请求到达容器,容器调用servlet对象的service()方法,处理请求的方法在整个生命周期中可以被多次调用;HttpServlet的service()方法,会依据请求方式来调用doGet()或者doPost()方法。但是,这两个do方法默认情况下,会抛出异常,需要子类去override。
  • 销毁时机
    • 当容器关闭时(应用程序停止时),会将程序中的Servlet实例进行销毁

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

演示生命周期:定义一个Servlet,继承HttpServlet后重写如下三个方法:

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

java 复制代码
public void init() throws ServletException {
    System.out.println("Servlet初始化");
}

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

java 复制代码
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
    System.out.println("处理对应业务 ");
}

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

java 复制代码
public void destroy() {
    System.out.println("Servlet对象被销毁");
}

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

java 复制代码
public Servlet04(){
    System.out.println("构造器函数被调用");
}

上述过程完整代码

java 复制代码
package com.study.testservlet;

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

@WebServlet("/s04")
public class Servlet04 extends HttpServlet {
    public Servlet04(){
        System.out.println("Servlet-used");
    }

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

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

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println("Servlet-warking");
    }
}

Servlet的生命周期

  1. Web client向Servlet容器(Tomcat)发出Http请求
  2. Servlet容器接收Web Client的请求
  3. 根据反射构建Servlet对象(第一次请求会构建,之后不会重复创建对象,直到服务器关闭才会被销毁)
  4. 会构建HttpServletRequest对象、HttpServletResponse对象,处理请求和响应
  5. Servlet容器会调用Servlet对象的servlet方法,把HttpServletRequest对象与HttpServletResponse对象作为service的参数,供给方法内部使用
  6. Servlet对象调用HttpServletRequest对象的有关方法,获取Http请求信息
  7. Servlet对象调用HttpServletResponse对象的有关方法,生成响应数据
  8. Servlet容器把HttpServlet的响应结果传给Web Client

2.6 HttpServletRequest对象

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

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

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

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

2.6.1 获取请求参数

方法

|---------------------------------|--------------|
| getParameter(name) | 获取指定名称的参数 |
| getParameterValues(String name) | 获取指定名称参数的所有值 |

示例

java 复制代码
String params = req.getParameter("username");
System.out.println("<getParameter>" + params);
String[] hobies = req.getParameterValues("hoby");
System.out.println("<getParameterValues>" + Arrays.toString(hobies));
2.6.2 请求乱码问题

在接收请求参数的时候,我们发现一个问题:当我们的值是中文的时候,系统就出现了乱码问题,如下:

并且,无论是get方式还是post方式,后台都会出现乱码的问题,出现乱码的原因:

如何解决呢?

java 复制代码
new String(req.getParameter("username").getBytes("iso-8859-1"), "utf-8");

实现过程

req.getParameter("username").getBytes("iso-8859-1") ---》 先将字符串使用iso-8859-1码转换成字节

new String(req.getParameter("username").getBytes("iso-8859-1"), "utf-8") ---》 然后将字节以UTF-8转成字符串

但是上面的处理方式有缺点:

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

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

java 复制代码
req.setCharacterEncoding("UTF-8");

但是这种解决方案只能解决post请求参数乱码的问题,在get请求中还是乱码

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

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

2.3.3 请求转发

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

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

(1)在/web创建login.jsp文件

html 复制代码
<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2025/12/17
  Time: 10:18
  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="/s/sr02" method="post">
        <label>用户名</label>
        <input type="text" name="uname"/>
        <input type="submit" value="登录">
    </form>
</body>
</html>

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

第一个

java 复制代码
package com.study.testrequest;

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

@WebServlet("/sr02")
public class ServletRequest02 extends HttpServlet {
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
//        req.setCharacterEncoding("UTF-8");
        String uname = req.getParameter("uname");
        System.out.println("sr02 ------ uname value: " + uname);
        // 请求转发
        req.getRequestDispatcher("sr03").forward(req, res);
        // 在请求转发之后输出一句话
        System.out.println("sr3 -------- requstDispatcher ");
    }
}

第二个

java 复制代码
package com.study.testrequest;

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

@WebServlet("/sr03")
public class ServletRequest03 extends HttpServlet {
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
//        req.setCharacterEncoding("UTF-8");
        // 获取请求数据
        String uname = req.getParameter("uname");
        System.out.println("sr03 ----- getUname" + uname);
    }
}

(3)启动浏览器,在浏览器发送请求http://localhost:8080/s/login.jsp

后台结果:

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

java 复制代码
req.getRequestDispatcher("index.jsp").forward(req, res); // 转发到某个页面
2.6.4 HttpServletRequest对象的使用

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

java 复制代码
// 这是域对象内容
req.setAttribute("age", "18");
// 获取域对象内容
req.getAttribute("age");
// 删除域对象内容
req.removeAttribute("age");

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

2.7 HttpServletResponse对象

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

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

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

2.7.1 响应数据

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

java 复制代码
// 字符输出流
res.getWriter().write("Hello World");
res.getWriter().print("Hello 1");
res.getWriter().println("Hello 2");

// 字节输出流
// res.getOutputStream().write("Hello".getBytes());
2.7.2 响应乱码问题

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

出现乱码的原因

解决方案

java 复制代码
// 设置传输过程中的编码格式
res.setCharacterEncoding("UTF-8"); // 先将字符串用ISO-8859-1码转为字节
// 告诉浏览器解码格式要使用UTF-8
res.setContentType("text/html;charset=UTF-8"); // 然后将该字节以UTF-8转成字符串
// 响应中文字符串给浏览器
res.getWriter().write("你好");
2.7.3 重定向

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

总结一下,重定向中会有两个请求存在,并且属于客户端行为。

2.7.4 请求转发与重定向的区别
不同点 请求转发 重定向
语法不同 req.getRequestDispatcher("sr03").forward(req, res); res.sendRedirect("red02")
请求不同 是同一个请求 不是同一个请求
地址栏是否发送改变 不变 改变
请求参数是否能携带 可以 不可以,但是可以通过手动设置携带参数:res.sendRedirect("red02?uname="+req.getParameter("uname"))
执行效率不同 服务器内部跳转,所以效率高 相对效率低
跳转范围不同 只能在服务器内部跳转 既能在服务器内部跳转,又能跳转服务器外部的资源
表单是否会发生重复提交 会,值得时数据提交后,刷新页面,因为地址栏地址不变,每刷新一次都会重复请求一次,数据提交一次。如果刚好是个添加操作,每次都会添加一次 不会
路径书写方式不同 绝对路径 不支持 支持
路径书写方式不同 相对路径
路径书写方式不同 根路径 项目根目录下查找资源 http:localhost:8880/项目访问名/ 服务器根目录下查找资源 http://localhost:8880
2.7.5 转发和重定向使用场景
  • 跳转前后是否需要共享request中的数据、
    • 如果需要共享:转发
    • 如果不需要:都可以
  • 是否需要跳转到服务器外部资源
    • 如果需要跳出项目:重定向
    • 如果不需要:都可以
  • 是否涉及表单重复提交
    • 重定向:不会造成表单重复提交
    • 转发:会造成表单重复提交
相关推荐
星星不打輰2 小时前
SSM项目--SweetHouse 甜蜜蛋糕屋
java·spring·mybatis·ssm·springmvc
Knight_AL2 小时前
Java 线程池预热(Warm-up)实战:开启与不开启到底差多少?
java·开发语言
爬山算法2 小时前
Netty(15)Netty的线程模型是什么?它有哪些线程池类型?
java·后端
小白阿龙2 小时前
Flex布局子元素无法垂直居中
前端
C++业余爱好者2 小时前
公司局域网访问外网的原理
java
秋田君2 小时前
前端工程化部署入门:Windows + Nginx 实现多项目独立托管与跨域解决方案
前端·windows·nginx
@淡 定2 小时前
异常处理最佳实践
java
一起养小猫2 小时前
LeetCode100天Day1-字符串匹配与Z字形变换
java·leetcode
江城开朗的豌豆2 小时前
阿里邮件下载器使用说明
前端