【JAVAWEB复习系列】第二部分

7 HTTP

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

浏览器和服务器之间数据传输的情况:

浏览器向服务器发送请求,服务器向浏览器提交响应。

作为HTTP而言,就是要让双方看得懂对方在说什么。

HTTP 协议特点:

1.基于TCP协议:面向连接,安全

【我们在复习java时复习过了协议,TCP协议是传输控制协议,传输需要建立连接,三次握手】

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

3.HTTP协议是无状态的协议:对于事务处理没有记忆能力。每次请求-响应都是独立的。

缺点:多次请求间不能共享数据。 优点:速度快

记忆:HTTP 协议特点是:安全,1v1,记不住,因为1V1所以无法共享,但是自己干自己的,所以速度快。

【如何解决不能共享数据?使用会话技术(Cookie、Session)来解决】

7.1 HTTP-请求数据格式

请求数据分为3部分:

1.请求行:请求数据的第一行。

其中GET表示请求方式,/表示请求资源路径,HTTP/1.1表示协议版本

2.请求头:第二行开始,格式为key:value形式。

3.请求体: POST请求的最后一部分,存放请求参数

案例:

GET / HTTP/1.1 
Host: www.itcast.cn 
Connection: keep-alive 
User-Agent: Mozilla/5.0 Chrome/91.0.4472.106
…
-------------------------
POST / HTTP/1.1 
Host: www.itcast.cn 
Connection: keep-alive 
Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 
User-Agent: Mozilla/5.0 Chrome/91.0.4472.106
username=superbaby&password=123456

7.1.1 常见的HTTP请求头

Host: 表示请求的主机名

记忆:LocalHost经常见到,是主机,所以Host表示请求的主机名】

User-Agent: 浏览器版本,例如Chrome浏览器的标识类似Mozilla/5.0 ... Chrome/79,IE浏览器的标识类似Mozilla/5.0 (Windows NT ...) like Gecko;

记忆:Agent是代理人,用户代理人,就是用户通过浏览器去看页面,那么含义就是这个代理是什么浏览器,它的版本是什么

Accept:表示浏览器能接收的资源类型,如text/*,image/*或者*/*表示所有;

记忆:Agent是代理人,用户代理人,就是用户通过浏览器去看页面,那么含义就是这个代理是什么浏览器,它的版本是什么

Accept-Language:表示浏览器偏好的语言,服务器可以据此返回不同语言的网页;

记忆:平常使用浏览器的时候,如果是外文网页会有自动翻译,所以浏览器是会需要知道网页的语言的

Accept-Encoding:表示浏览器可以支持的压缩类型,例如gzip, deflate等。

记忆:因为TCP协议会进行大数据量的传输,所以大数据的编码要压缩

7.2 HTTP-响应数据格式

响应数据分为3部分:

1.响应行:响应数据的第一行。

        其中HTTP/1.1表示协议版本,200表示响应状态码,OK表示状态码描述

2.响应头:第二行开始,格式为key:value形式

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

案例:

HTTP/1.1 200 OK
Server: Tengine
Content-Type: text/html
Transfer-Encoding: chunked…
<html>
<head>
     <title></title>
</head>
<body></body>
</html>

状态码可以在此连接查询:状态 | Status - HTTP 中文开发手册 - 开发者手册 - 腾讯云开发者社区-腾讯云

7.2.1 常见的HTTP 响应头:

Content-Type:表示该响应内容的类型,例如text/html,image/jpeg;

Content-Length:表示该响应内容的长度(字节数);

Content-Encoding:表示该响应压缩算法,例如gzip;

Cache-Control:指示客户端应如何缓存,例如max-age=300表示可以最多缓存300秒

7.3 对照记忆

过程请求数据响应数据
GET请求POST请求
格式1.请求行

1.响应行

GET / HTTP/1.1POST / HTTP/1.1HTTP/1.1 200 OK
2.请求头

2.响应头

key:value
\3.请求体3.响应体
\请求参数响应数据

8 Web 服务器

定义:Web服务器是一个应用程序(软件)。

功能:

  • 封装HTTP协议操作,简化开发  
  • 可以将web项目部署到服务器中,对外提供网上浏览服务

优点:对 HTTP协议的操作进行封装,使得程序员不必直接对协议进行操作,让Web开发更加便捷。

8.1 Tomcat服务器

定义:Tomcat是Apache 软件基金会一个核心项目,是一个开源免费的轻量级Web服务器,支持Servlet/JSP少量JavaEE规范。

Tomcat 也被称为 Web容器、Servlet容器。Servlet 需要依赖于 Tomcat才能运行

JavaEE:Java Enterprise Edition,Java企业版。指Java企业级开发的技术规范总和。包含13项技术规范:JDBC、JNDI、EJB、RMI、JSP、Servlet、XML、JMS、Java IDL、JTS、JTA、JavaMail、JAF

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

8.2 Tomcat使用

这部分没什么知识性的东西。

需要了解:下载、安装、卸载、启动、关闭、配置、部署项目。

IDEA中使用 Tomcat – Tomcat Maven 插件:

<build>
    <plugins>
      <!--Tomcat 插件-->
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
        <configuration>
          <port>8081</port><!--访问端口号-->
          <path>/</path><!--项目访问路径-->
        </configuration>
      </plugin>
    </plugins>
  </build>

9 Servlet

定义:Servlet 是 Java提供的一门动态web资源开发技术。

Servlet 是JavaEE 规范之一,其实就是一个接口,我们需要定义Servlet类实现Servlet接口,并由web服务器运行Servlet

9.1 Servlet的使用与执行流程初探

9.1.1 使用Servlet

1.创建 web项目,导入 Servlet依赖坐标

<dependencies>
  <dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>这里一定要设置成provided
  </dependency>
</dependencies>

2.定义一个类,实现 Servlet接口,并重写接口中所有方法,并在 service方法中输入一句话

public class ServletDemo1 implements Servlet {
    public void service(){
        System.out.println("servlet被执行");
    }
}  

3.配置:在类上使用@WebServlet 注解,配置该 Servlet的访问路径

@WebServlet("/demo1")
public class ServletDemo1 implements Servlet {}

4.访问:启动 Tomcat,浏览器输入URL 访问该Servlet

http://localhost:8081/web-demo/demo1

9.1.2 Servlet执行流程

输入了URL之后,浏览器向服务器发送请求

其中“http://localhost:8081”可以寻找到Tomcat这个web服务器,“web-demo”可以寻找到WEB项目,“demo1”寻找到对应的Servlet实现类

在此过程中,web服务器创建Servlet对象,调用Servlet方法。

之后,服务器向浏览器响应结果。

9.2 Servlet生命周期

生命周期:对象的生命周期指一个对象从被创建到被销毁的整个过程

Servlet运行在Servlet容器(web服务器)中,其生命周期由容器来管理,分为4个阶段:

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

        @WebServlet(urlPatterns = "/demo",loadOnStartup = 1)

        loadOnStartup的值设置为:负整数:第一次被访问时创建Servlet对象

        0或正整数:服务器启动时创建Servlet对象,数字越小优先级越高

记忆:不管一个什么东西,拿过来用之前先加载一下非常合情合理,并且web服务器创建servlet对象之前也提到过了。默认情况下访问之后才会创建,而如果要启动创建才需要设置参数值,因此它是不同的特殊情况,而越小的数字在前面也很合理

2.初始化:在Servlet实例化之后,容器将调用Servlet的init()方法初始化这个对象,完成一些如加载配置文件、创建连接等初始化的工作。该方法只调用一次

记忆:Servlet中有五个方法,init()就是用来初始化的,而初始化一次就行了

3.请求处理:每次请求Servlet时,Servlet容器都会调用Servlet的service()方法对请求进行处理。

记忆:service方法之前已经在初探中展示过了,毕竟作为servlet总得干活

4.服务终止:当需要释放内存或者容器关闭时,容器就会调用Servlet实例的destroy()方法完成资源的释放。在destroy()方法调用之后,容器会释放这个Servlet实例,该实例随后会被Java的垃圾收集器所回收

记忆:干完活就得终止了,释放资源嘛

9.3 Servlet方法

1.初始化方法,在Servlet被创建时执行,只执行一次

void init(ServletConfig config)

2.提供服务方法, 每次Servlet被访问,都会调用该方法

void service(ServletRequest req, ServletResponse res)

3.销毁方法,当Servlet被销毁时,调用该方法。在内存释放或服务器关闭时销毁Servlet。只执行一次

void destroy()

4.获取ServletConfig对象

ServletConfig getServletConfig()

5.获取Servlet信息。一般没什么,我们让它return null;即可

String getServletInfo()

在此我们重点讲一下如何获取某个方法的入参。

@WebServlet(urlPatterns = "/demo3",loadOnStartup = 1)
public class ServletDemo3 implements Servlet {
    private ServletConfig servletConfig;
初始化方法 
    public void init(ServletConfig servletConfig) throws ServletException {
        this.servletConfig = servletConfig;
        System.out.println("init被调用");
    }
获取ServletConfig对象方法
    public ServletConfig getServletConfig() {
        return servletConfig;
    }
}

想获取init的servletConfig,可以设定一个成员变量,将值赋给成员变量,这样的话getservletConfig方法可以返回这个变量的值给调用它的对象。

9.4 Servlet 体系结构

Servlet接口作为Servlet体系根接口

GenericServlet是Servlet抽象实现类

HttpServlet是对HTTP协议封装的Servlet实现类

在9.1.1中,我们自定义的Servlet类实现了跟体系接口,但开发B/S架构的web项目,都是针对HTTP协议的,所以之后自定义Servlet,要继承HttpServlet类。

9.4.1 HttpServlet 原理

在HTTP协议中,GET 和 POST 请求方式的数据格式不一样,所以在Servlet中处理请求参数,得在service方法中判断请求方式,并且根据请求方式的不同,分别进行处理。

9.4.2 HttpServlet 抽象方法展示

@WebServlet("/demo4")
public class ServletDemo4 extends HttpServlet {
    @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===");
    }
}

其中两个方法,分别针对get请求和post请求进行处理

9.5 Servlet urlPattern配置

之前我们已经展示过例如“@WebServlet("/demo4")”这样的访问路径设置了,我们知道:

Servlet 要想被访问,必须配置其访问路径(urlPattern)

1.一个Servlet,可以配置多个 urlPattern,以数值的形式赋值

例如:

@WebServlet(urlPatterns = {"/demo7","/demo8"})

2.urlPattern有四种配置规则

  1. 精确匹配
  2. 目录匹配
  3. 扩展名匹配
  4. 任意匹配(不建议使用)

四种匹配规则的优先级:精确路径 > 目录路径 > 扩展名路径 > /* > /

9.5.1 urlPattern 配置规则 精确匹配

配置路径:@WebServlet(“/user/select”)

访问路径:localhost:8081/web-demo/user/select

也就是说,访问的路径和配置的路径需要一模一样

9.5.2 urlPattern 配置规则 目录匹配

配置路径:@WebServlet(“/user/*”)

访问路径:

  • localhost:8081/web-demo/user/abc
  • localhost:8081/web-demo/user/def

写到user是要一样的,但*代表的就是随便写什么都能匹配到这个servlet

9.5.3 urlPattern 配置规则 扩展名匹配

配置路径:@WebServlet(“*.do”)

访问路径:

  • localhost:8081/web-demo/abc.do
  • localhost:8081/web-demo/def.do

需要有和配置路径一样的扩展名

9.5.4 urlPattern 配置规则 任意匹配

配置路径:

  • @WebServlet(“/”)
  • @WebServlet(“/*”)

访问路径:

  • localhost:8081/web-demo/abcdeg
  • localhost:8081/web-demo/sfjewjghsjd

随便写

/ 和 /* 区别:

  • 当我们的项目中的Servlet配置了“/”,会覆盖掉tomcat中的DefaultServlet,当其他的 url-pattern都匹配不上时都会走这个Servlet
  • 当我们的项目中配置了“/*”,意味着匹配任意访问路径

9.6 XML 配置方式编写 Servlet

Servlet 从3.0版本后开始支持使用注解配置,3.0版本前只支持 XML 配置文件的配置方式

这种方式比较麻烦,建议使用注解编写

使用步骤:

1.编写 Servlet类

2.在 src/main/webapp/WEB-INF/web.xml中配置该Servlet

案例:

<!--  Servlet全类名-->
  <servlet>
    <servlet-name>demo13</servlet-name>
    <servlet-class>com.xuexi.web.servletdemo13</servlet-class>
  </servlet>
<!--  Servlet访问路径-->
  <servlet-mapping>
    <servlet-name>demo13</servlet-name>
    <url-pattern>/demo13</url-pattern>
  </servlet-mapping>

10 Request与Response

在上一章中我们学习了Servlet其中有两个参数Request,Response

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

记忆:request对象就从请求中拿东西来用的,他请求啥,如果合适的话就给他看

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

记忆:来的时候tomcat把数据解析成“体头行”,走的时候也得把他拼接成体头行返回回去

小结:

  • request对象是用来封装请求数据的对象
  • response对象是用来封装响应数据的对象

10.1 Request对象继承体系

ServletRequest接口   Java提供的请求对象根接口

HttpServletRequest接口   Java提供的 对Http协议封装的请求对象接口

RequestFacade类    Tomcat定义的实现类

因此:RequestFacade类实现了HttpServletRequest接口,也间接实现了ServletRequest接口。

Servlet 类中的 service 方法、 doGet 方法、 doPost 方法最终都是由 Web 服务器[Tomcat]来调用的,Tomcat 提供了方法参数接口的具体实现类,并完成了对象的创建

查看RequestFacade的方法可以查询“JavaEE的API文档中关于ServletRequest和HttpServletRequest的接口文档

10.2 Request获取请求数据

HTTP请求数据分为 请求行、请求头、请求体 三部分,接下来我们分别学习Request对象的这些方法

10.2.1 获取请求行数据

请求行包含:请求方式,请求资源路径以及HTTP协议版本

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

10.2.2 获取请求头数据

请求头的数据格式为key:value

如:User-Agent:Mozilla/5.0 Chrome/91.0.4472.106

方法功能
String getHeader(String name)
根据请求头名称获取对应值

10.2.3 获取请求体数据

只有POST请求具有请求体,请求体中数据的格式如:

username=zhangsan&password=123
方法功能
ServletInputStream getInputStream()
获取字节输入流
BufferedReader getReader()
获取字符输入流【当前端发送的是纯文本数据,建议使用此方法】

使用案例:

1.编写一个html页面【位置:src/main/webapp文件夹中】,在页面中设置表单,提交表达则会发送POST请求

<body>
<form action="/tomcatdemo/req3" method="post">
    <input type="text" name="username"><br>
    <input type="password" name="password"><br>
    <input type="submit">
</form>

其中:action:form表单提交的请求地址 ;method:请求方式,指定为post


路径

1.究竟如何写路径,何时要加上虚拟目录(项目访问路径),何时不加?

判断的依据:
  • 浏览器使用 : 需要加虚拟目录 ( 项目访问路径 )
  • 服务端使用 : 不需要加虚拟目录

示例:

  • 对于转发来说,因为是在服务端进行的,所以不需要加虚拟目录
  • 对于重定向来说,路径最终是由浏览器来发送请求,就需要添加虚拟目录。

 2.Maven中配置了项目的访问地址,如何解耦?

使用request对象中的getContextPath()方法,如下

简化方式完成重定向 
动态获取虚拟目录 
String contextPath = request.getContextPath(); 
response.sendRedirect(contextPath+"/resp2");

2.ServletdoPost方法中获取数据

【创建继承HttpServlet类的Servlet类的快捷方法为:在包上右键-new-(往下找)Create new servlet】

@WebServlet("/req3")
public class RequestDemo1 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        获取请求体:请求参数
        获取字符输入流
        BufferedReader reader = req.getReader();
        String s = reader.readLine();
        System.out.println(s);
    }
}

注意:BufferedReader流是通过request对象来获取的,当请求完成后request对象就会被销毁,

request 对象被销毁后, BufferedReader 流就会自动关闭,所以此处就不需要手动关闭流了。

3.启动服务器,通过浏览器访问http://localhost:8081/demo/req3.html,提交表单发送POST请求

4.在控制台查看请求,已经获取到了。

10.2.4 统一获取GET/POST请求参数

由于在获取请求参数时,doGet和doPost的方法中存在大量重复代码,因此在实际使用过程中可以通过这种方式:

@WebServlet("/req2")
public class RequestDemo2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req,resp);
    }
}

解决GET请求和POST请求获取请求参数的方式不一样问题。

1.首先,两种请求参数的方式都将获取到例如:username=zhangsan&password=123 形式的请求参数。

2.将获取到的参数进行分割,可以得到若干个键与值

3.将若干键与值存放进Map集合中,此Map集合的泛型为Map<String,String[]>

因为参数的值可能是一个,也可能有多个,所以Map的值的类型为String数组。

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

使用案例:

@WebServlet("/req2")
public class RequestDemo2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        1.获取所有参数的map集合
        Map<String, String[]> map = req.getParameterMap();
        for (String key : map.keySet()) {
            希望得到username:zhangsan这样的格式
            System.out.print(key+":");
            获取值
            String[] values = map.get(key);
            for (String value : values) {
                System.out.print(value+"");
            }
            System.out.println();//换行用
        }
        2.根据key获取参数值,数组
        String[] hobbies = req.getParameterValues("hobby");
        for (String hobby : hobbies) {
            System.out.println(hobby);
        }
3.根据key获取单个参数值
        String name = req.getParameter("username");
        System.out.println(name);
        String pw = req.getParameter("password");
        System.out.println(pw);
    }

小结:

在实际运用过程中采用request提供的获取请求参数的通用方式来获取请求参数,建议使用“this.doGet(req,resp); ”仅在doGet方法中写一遍处理逻辑即可

10.3 解决请求参数中文乱码

10.3.1 POST请求参数乱码

中文参数乱码的原因:

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

解决方案:

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

案例:

<head>第一步
    <meta charset="UTF-8">
    <title>Title</title>
</head>
1. 解决乱码: POST getReader() 
设置字符输入流的编码,设置的字符集要和页面保持一致 
request.setCharacterEncoding("UTF-8"); 
2. 获取username
String username = request.getParameter("username");

10.3.2 GET请求参数乱码

由于GET请求获取请求参数的方式是request.getQueryString(),它不是通过流的方式获取数据的,因此无法参考POST请求乱码的方式

解决思路:

1.乱码原因:

(1)浏览器通过HTTP协议发送请求和数据给后台服务器(Tomcat)

(2)浏览器在发送HTTP的过程中会对中文数据进行URL编码

(3)在进行URL编码的时候会采用页面<meta>标签指定的UTF-8的方式进行编码,如:张三编码后的结果为%E5%BC%A0%E4%B8%89

(4)后台服务器(Tomcat)接收到%E5%BC%A0%E4%B8%89后会默认按照ISO-8859-1进行URL解码

(5)由于前后编码与解码采用的格式不一样,就会导致后台获取到的数据为乱码

2.URL编码URL解码了解

2.1URL编码过程

(1)将字符串按照编码方式转为二进制

(2)每个字节转为216进制数并在前边加上%

方法功能
String  URLEncoder.encode( " 需要被编码的内容 " , " 字符集 (UTF-8)" )
URL编码
String   URLDecoder . decode ( " 需要被解码的内容 " , " 字符集 (UTF-8)" )
URL 解码

案例:

public static void main(String[] args) throws UnsupportedEncodingException { 
    String username = "张三"; 
 URL编码 
 String encode = URLEncoder.encode(username, "utf-8"); 打印:%E5%BC%A0%E4%B8%89 89 
 URL解码 
String decode = URLDecoder.decode(encode, "utf-8");打印:张三 
String decode = URLDecoder.decode(encode, "ISO-8859-1");打印:`å¼ ä¸ ` 
 System.out.println(decode); 
}}

3.解决思路

不论使用何种编码、解码方式,“%E5%BC%A0%E4%B8%89”是相同的,二进制数是相同的,先将乱码转换成字节,由字节转换为“张三”即可。

public static void main(String[] args) throws UnsupportedEncodingException {
        String username = "张三";
1. URL编码【浏览器对用户输入的中文进行编码】
        String encode = URLEncoder.encode(username, "utf-8");
        System.out.println(encode);
2. URL解码【tomcat对浏览器提交的参数进行解码】
        String decode = URLDecoder.decode(encode, "ISO-8859-1");
        System.out.println(decode);
3. 转换为字节数据,编码【我们解决问题从这里开始】
        byte[] bytes = decode.getBytes("ISO-8859-1");
4. 将字节数组转为字符串,解码
        String s = new String(bytes, "utf-8");
        System.out.println(s);
}

这个过程在我们的运用中可以简化为:

username  = new String(
username.getBytes(StandardCharsets.ISO_8859_1),
StandardCharsets.UTF_8);

我们主要学习解题思路,因为Tomcat8.0之后,已将GET请求乱码问题解决,设置默认的解码方式为UTF-8

10.4 Request请求转发

定义:请求转发(forward)是一种在服务器内部的资源跳转方式。

具体过程:

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

示例:

request.getRequestDispatcher("资源B路径").forward(request,response);
方法功能

RequestDispatcher  getRequestDispatcher(String path)

该方法返回一个RequestDispatcher对象,调用这个对象的forward方法可以实现请求转发
void  forward(ServletRequest request, ServletResponse response)RequestDispatcher对象的方法。将请求从Servlet转发到服务器上的另一个资源(Servlet,JSP文件或HTML文件)
void setAttribute(String name,Object o);
存储数据到 request 域【 范围 , 数据是存储在 request 对象】
Object getAttribute(String name);
根据 key 获取值
void removeAttribute(String name);
Object getAttribute(String name);

使用案例:在转发请求之前存放一些数据到request域中,并在另一个Servlet中获取数据

@WebServlet("/req4")
public class RequestDemo4 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
存储数据
        request.setAttribute("msg","hello");
请求转发
        request.getRequestDispatcher("/req5").forward(request,response);
}}
@WebServlet("/req5")
public class RequestDemo5 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
获取数据
        Object msg = request.getAttribute("msg");
    }
}
请求转发的特点:

1.浏览器地址栏路径不发生变化

虽然后台从/req4转发到/req5,但是浏览器的地址一直是/req4,未发生变化

2.只能转发到当前服务器的内部资源

【不能从一个服务器通过转发访问另一台服务器】

3.一次请求,可以在转发资源间使用request共享数据

【虽然后台从/req5转发到/req6,但是这个只有一次请求】

10.5 Response对象继承体系

我们在学习Response时会尽可能与request对比学习,这样有利于记忆

request对象继承体系Response对象继承体系
ServletRequest接口Java提供的请求对象根接口ServletResponseJava提供的响应对象根接口
HttpServletRequest接口Java提供的对Http协议封装的请求对象接口

HttpServletResponse

接口

Java提供的对Http协议封装的响应对象接口
RequestFacade类Tomcat定义的实现类ResponseFacade类Tomcat定义的实现类

10.6 Response设置响应数据

HTTP响应数据分为响应行、响应头、响应体三部分。接下来我们分别学习 Response对象的方法,如下:

10.6.1 响应行

响应行由HTTP协议及版本,响应状态码以及状态码的描述组成

方法作用
void setStatus(int sc);
设置响应状态码

10.6.2 响应头

响应头的数据格式为键值对,key:value

方法作用
void setHeader(String name,String value);
设置响应头键值对

10.6.3 响应体

响应体需要通过字符、字节输出流的方式写出到浏览器

方法作用
PrintWriter getWriter();
获取字符输出流
ServletOutputStream getOutputStream();
获取字节输出流

1.Response响应字符数据

要将字符数据写回到浏览器有如下两个步骤:

  1. 通过Response对象获取字符输出流: PrintWriter writer = resp.getWriter();
  2. 通过字符输出流写数据: writer.write("aaa");
@WebServlet("/resp3")
public class responsedemo3 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
防止中文乱码
        response.setContentType("text/html;charset=utf-8");
获取字符输出流
        PrintWriter writer = response.getWriter();
解析html标签
        response.setHeader("content-type","text/html");
        writer.write("你好");
        writer.write("<h1>bbbb</h1>");
        细节:流无需关闭
}}

 由于一次请求响应结束后,response对象就会被销毁掉,所以不要手动关闭流。

2.Response响应字节数据

 要将字节数据写回到浏览器有如下两个步骤:

  1. 通过Response对象获取字节输出流:
    ServletOutputStream outputStream = resp.getOutputStream();
  2. 通过字节输出流写数据: outputStream.write(字节数据);
@WebServlet("/resp4")
public class responsedemo4 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
1.读取文件
        FileInputStream is = new FileInputStream("e://图像 001.png");
2.获取字节输出流
        ServletOutputStream os = response.getOutputStream();
3.完成流的copy
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len = is.read(buffer))!= -1){
            os.write(buffer,0,len);
        }
        is.close();
}}

 以上代码过于繁琐,简化方法如下

1.在pom.xml添加依赖 commons-io,如

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

2.调用工具类方法

@WebServlet("/resp4")
public class responsedemo4 extends HttpServlet {
 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
1.读取文件
        FileInputStream is = new FileInputStream("e://图像 001.png");
2.获取字节输出流
        ServletOutputStream os = response.getOutputStream();
3.调用工具类方法
        IOUtils.copy(is,os);
        is.close();
}}

10.7 Respones请求重定向

定义:Response重定向(redirect):一种资源跳转方式。

Request请求转发 Respones请求重定向
定义请求转发(forward)是一种在服务器内部的资源跳转方式。重定向(redirect)是一种资源跳转方式。
过程

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

(1)浏览器发送请求给服务器,服务器中对应的资源A接收到请求
(2)资源A现在无法处理该请求,就会给浏览器响应一个302的状态码+location的一个访问资源B的
路径
(3)浏览器接收到响应状态码为302就会重新发送请求到location对应的访问地址去访问资源B
(4)资源B接收到请求后进行处理并最终给浏览器响应结果,这整个过程就叫重定向
 
实现方式request.getRequestDispatcher("资源B路径").forward(request,response);response.setStatus(302); 
response.setHeader("location","资源B的访问路径");

使用案例:设置重定向状态码到响应头中,重定向到另一个Servlet中获取数据

@WebServlet("/resp1")
public class responsedemo1 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {       
        重定向
1.设置响应状态码 302
        response.setStatus(302);
2.设置响应头 Location 不区分大小写 要加虚拟目录名称
        response.setHeader("location","/tomcatdemo/resp2");
        以上可以化简为
        response.sendRedirect("/tomcatdemo/resp2");
        动态获取虚拟目录,最终化简为如下:
        String contextPath = request.getContextPath();
        response.sendRedirect(contextPath+"/resp2");
    }
}
Request请求转发 Respones请求重定向
特点

1.浏览器地址栏路径不发生变化

1.浏览器地址栏路径发生变化

当进行重定向访问的时候,由于是由浏览器发送的两次请求,所以地址会发生变化

2.只能转发到当前服务器的内部资源

2.可以重定向到任何位置的资源(服务内容、外部均可)

【因为第一次响应结果中包含了浏览器下次要跳转的路径,所以这个路径是可以任意位置资源。】

3.一次请求,可以在转发资源间使用request共享数据3.两次请求,不能在多个资源使用request共享数据【因为浏览器发送了两次请求,是两个不同的request对象,就无法通过request对象进行共享数

根据请求转发和请求重定向的特点,具体问题具体分析,选择更合适的方式处理请求。

10.8 SqlSessionFactory工具类抽取

由于需要使用Mybatis来完成数据库的操作,在每一个Servlet中都写一遍:

String resource = "mybatis-config.xml"; 
InputStream inputStream = Resources.getResourceAsStream(resource); 
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

显然是不行的,重复代码不利于后期的维护 ,SqlSessionFactory工厂类进行重复创建使资源消耗非常严重

解决方案:

  • 代码重复可以抽取工具类
  • 对指定代码只需要执行一次可以使用静态代码块
public class SqlSessionFactoryUtils {
//想在一个代码块中使用另一个代码块的局部变量,就要这样提升作用域
    private  static  SqlSessionFactory sqlSessionFactory;
    //写入静态代码块后就只会执行一次了,因为不能抛出异常所以用try/catch
    static {
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
//            赋值
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
}}
    public static SqlSessionFactory getSqlSessionFactory(){
//        赋值了之后可以将其返回
        return sqlSessionFactory;
}}

从此可以使用一下代码替换那三行代码

SqlSessionFactory sqlSessionFactory =SqlSessionFactoryUtils.getSqlSessionFactory();

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值