转发_重定向

1.Servlet/JSP单独使用的弊端

当我们用Servlet或者JSP单独处理请求的时候

  • Servlet:拼接大量的html字符串 造成可读性差、难以维护
  • JSP:使得html和Java代码互相交织 也造成了可读性差、难以维护的后果

最合适的做法就是两者结合使用

2.Servlet+JSP处理请求的常见过程

在这里插入图片描述

  • 以上过程就是典型的MVC开发模式 即Model(数据)-View(页面展示)-Controller(控制器)模式 分别对应着上图中的数据库-JSP-Servlet

3.实现

  • 有了前面这层铺垫 再加上Servlet_JSP中介绍的EL和JSTL 那么我们就可以开始实现以下我们的crm项目了 即customer relationship management(客户关系管理系统) 我们采用的方式就是Servlet和JSP结合使用
  • 我们首先先来实现一下Servlet中不拼接html字符串、JSP中不内嵌Java代码的需求 即Servlet转发到JSP这一步
    Customer.java
    package com.mj.bean;
    
    public class Customer {
        // 定义成员变量
        // 姓名
        private String name;
        // 年龄
        private Integer age;
        // 身高
        private Double height;
    
        public Customer(String name, Integer age, Double height) {
            this.name = name;
            this.age = age;
            this.height = height;
        }
    
        public Customer() {
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public Double getHeight() {
            return height;
        }
    
        public void setHeight(Double height) {
            this.height = height;
        }
    
        @Override
        public String toString() {
            return "Customer{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", height=" + height +
                    '}';
        }
    }
    
    ListServlet.java
    import com.mj.bean.Customer;
    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;
    import java.util.ArrayList;
    import java.util.List;
    @WebServlet("/list")
    public class ListServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 将用户信息集合以键值对的形式储存到request中
            request.setAttribute("customers", getCustomers());
            // 然后将请求发送给JSP 并且跳转到JSP页面
            request.getRequestDispatcher("page/list.jsp").forward(request, response);
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doPost(request, response);
        }
        // 定义一个获取用户信息的方法
        private List getCustomers(){
            // 首先我们创建一个List集合 用于储存用户信息
            List<Customer> customers = new ArrayList<>();
            // 我们还是假设有10个用户
            for(int i = 0; i < 10; ++i){
                customers.add(new Customer("张三" + i, 10 + i, 1.70 + i));
            }
            // 返回用户信息集合
            return customers;
        }
    }
    
    List.jsp
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
    <html>
    <head>
        <title>展示用户信息</title>
        <!-- 设置表格单元格的样式 -->
        <style>
            th td {
                border: 1px solid black;
            }
        </style>
    </head>
    <body>
    <!-- 我们要以表格的形式来展示用户信息 -->
    <table>
        <thead>
        <tr>
            <th>姓名</th>
            <th>年龄</th>
            <th>身高</th>
        </tr>
        </thead>
        <tbody>
        <c:forEach items="${customers}" var="customer">
            <tr>
                <td>${customer.name}</td>
                <td>${customer.age}</td>
                <td>${customer.height}</td>
            </tr>
        </c:forEach>
        </tbody>
    </table>
    </body>
    </html>
    
    • 一些小细节:
      • Customer.java中的成员变量统统设置为包装类的原因在于方便判空 我举个例子 比如我设置了一个price成员变量 如果设置为int基本类型 那么就无法判空 相反 如果设置为了包装类Integer的话 那么判空操作就十分容易
      • getRequestDispacher() 可以接收一个参数 表示的是转换的目的地 由于转发操作只能在同一个context中进行 所以你只需要写明Servlet/JSP在当前context中的路径即可
      • 我们在jsp中导入标签库的时候 语句为<%@taglib prefix="c" uri="xxx"%> 其中uri指的是导入的标签库路径 而prefix则为使用标签库中的标签时所加的前缀 设置前缀的意义在于可以区分不同标签库中的相同标签 避免产生歧义
      • 我们利用el获取了customers集合 他的本质为request.getAttribute(“customers”) 但是获取集合中每一个元素的属性时 我们也利用了el进行获取 其本质为obj.getProperty(但是如果遍历的元素类型是Map类型并且要获取的是其中的value 那么el表达式通常写为obj[“property”]/obj[propertyVar] 其本质相当于Java中的obj.get 即Map中的键找值)

4.转发

  • 所谓转发 其实就是将请求发送给指定的接收方 并且跳转到接收方的界面
  • 转换操作只能在同一个context(项目 context就是用来定位部署到服务器软件上的项目的) 即只允许你在同一个项目中的不同Servlet/JSP(JSP的本质就是Servlet)进行转发操作

1.转发链条

  • 在同一个请求中 可以转发多次 形成一个转发链条

    • 在一个转发链条上 可以通过request.setAttribute、request.getAttribute进行数据共享(不同的context之间肯定是不可能发生数据共享的 原因在于转发操作只允许在同一个context中进行)
      在这里插入图片描述
    • 每一次转发都会创建一个新的request对象 用成员变量request指向前一个request对象
      ListServlet
    package mj.servlet;
    
    import mj.bean.Customer;
    
    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;
    import java.util.ArrayList;
    import java.util.List;
    
    @WebServlet("/list")
    public class ListServlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 将用户信息集合以键值对的形式储存到request中
            request.setAttribute("customers", getCustomers());
            // 然后将请求发送给JSP 并且跳转到JSP页面
            request.getRequestDispatcher("/test1").forward(request, response);
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doPost(request, response);
        }
        // 定义一个获取用户信息的方法
        private List getCustomers(){
            // 首先我们创建一个List集合 用于储存用户信息
            List<Customer> customers = new ArrayList<>();
            // 我们还是假设有10个用户
            for(int i = 0; i < 10; ++i){
                customers.add(new Customer("张三" + i, 10 + i, 1.70 + i));
            }
            // 返回用户信息集合
            return customers;
        }
    }
    

    Test1Servlet

    package mj.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;
    
    @WebServlet("/test1")
    public class Test1Servlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println(request);
            request.getRequestDispatcher("/test2").forward(request, response);
        }
    }
    

    Test2Servlet

    package mj.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;
    
    @WebServlet("/test2")
    public class Test2Servlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println(request);
            request.getRequestDispatcher("/test3").forward(request, response);
        }
    }
    

    Test3Servlet

    package mj.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;
    
    @WebServlet("/test3")
    public class Test3Servlet extends HttpServlet {
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            doGet(request, response);
        }
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            System.out.println(request);
        }
    }
    

    对于以上案例 我们在地址栏中输入test1 然后就可以看到三次转发相应的request类型以及哈希值 从结果来看 三次转发的request对象都各不相同 因此 可以证明每一次转发都创建了一个新的request对象
    在这里插入图片描述
    而在同一个转发链条中进行数据共享 则依靠的是每一次新的request对象中request成员变量会指向上一次的request对象 相当于通过两两之间的指针进行数据共享
    在这里插入图片描述
    我们可以通过断点调试证明一下上述图的合理性
    我们分别在三个TestServlet中request打印语句打断点 然后依次执行到三处断点位置 分别查看三个request对象 以下图片展示了转发链条上request对象之间的指向关系
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 在转发链条上 所有的attribute都会储存到头部的Request对象中

    • 实现原理:第一次转发的request对象是RequestFacade类型 我们查找该类中的setAttribute 可以发现 他其实是调用本类中的request成员的setAttribute 而本类中的request成员指向前一个request对象 那么我们就可以知道 原来他是一路往上调用setAttribute 直到调用头部request对象的setAttribute
    • 同理 attribute的获取也是一路往上调用getAttribute 直到调用头部request对象的getAttribute为止
      在这里插入图片描述

5.再次实现

这次的实现我们要完成添加和保存功能 其中 具体的思路为:list.jsp页面点击添加按钮 跳转到添加界面 输入新用户 保存 表单发送给SaveServlet处理 然后添加到ListServlet中的Customers集合 但是呢 有更好的解决方案 即设置一个模型(bean) 封装获取/添加集合的功能 只不过 里头的话 初始化集合需要放置在静态初始化块(保证程序运行过程中只会执行一次) 集合变量需私有静态修饰 并且以final加以修饰(保证程序运行过程中只有一份内存 且由于只进行了一次赋值操作 所以建议加上final修饰) 至于说获取/添加集合的功能 需公有化 提供外界去访问

list.jsp 其中 a的href属性值不要加/ 否则无法跳转

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>客户列表</title>
    <style>
        th {
            border: 1px solid black;
        }
        td {
            border: 1px solid red
        }
    </style>
</head>
<body>
<a href="page/add.html">添加</a>
<table>
    <thead>
        <tr>
            <th>姓名</th>
            <th>年龄</th>
            <th>身高</th>
        </tr>
    </thead>
    <tbody>
        <!-- 遍历集合中的元素 并且获取每一个元素的属性 -->
        <c:forEach items="${customers}" var="customer" varStatus="s">
            <tr>
                <td>${customer.name}_${s.index}</td>
                <td>${customer.age}_${s.index}</td>
                <td>${customer.height}_${s.index}</td>
            </tr>
        </c:forEach>
    </tbody>
</table>

</body>
</html>

add.html 其中 form的action属性值会拼接到IP:端口号之后 可以加/

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/crm3/save" method="post">
    <div>姓名 <input type="text" name="name"></div>
    <div>年龄 <input type="text" name="age"></div>
    <div>身高 <input type="text" name="height"></div>
    <div><button type="submit">保存</button></div>
</form>
</body>
</html>

SaveServlet.java 其中 getRequestDispatcher参数为相对于项目路径的路径 可以加/
而且 字符串转换为Integer/Double有两种方法 分别为:valueOf/parsexxx 返回值分别为Integer/int 建议使用一步到位的valueOf方法

package com.mj.servlet;

import com.mj.bean.Customer;
import com.mj.bean.Data;

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("/save")
public class SaveServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 设置请求类型
        request.setCharacterEncoding("UTF-8");
        // 接收请求参数
        String name = request.getParameter("name");
        String age = request.getParameter("age");
        String height = request.getParameter("height");
        // 接着我们要将请求参数封装为一个bean类
        Customer customer = new Customer(name, Integer.valueOf(age), Double.valueOf(height));
        Data.add(customer);
        request.getRequestDispatcher("/list").forward(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

ListServlet.java 其中 getRequestDispatcher的参数仍然是相对于项目路径的路径 可以加/

package com.mj.servlet;

import com.mj.bean.Customer;
import com.mj.bean.Data;

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;
import java.util.ArrayList;
import java.util.List;

@WebServlet("/list")
public class ListServlet extends HttpServlet {
    private List<Customer> getCustomers(){
        // 定义一个集合 用于存放customer
        List<Customer> customers = Data.getCustomers();
        return customers;
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 将获取到的用户信息储存到集合中
        request.setAttribute("customers", getCustomers());
        // 然后在转发到list.jsp
        request.getRequestDispatcher("/page/list.jsp").forward(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

Customer.java 用户信息类

package com.mj.bean;

public class Customer {
    // 姓名
    private String name;
    // 年龄
    private Integer age;
    // 身高
    private Double height;

    public Customer(String name, Integer age, Double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }

    public Customer() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Double getHeight() {
        return height;
    }

    public void setHeight(Double height) {
        this.height = height;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                '}';
    }
}

Data.java 封装用户获取/添加功能的类

package com.mj.bean;

import java.util.ArrayList;
import java.util.List;

public class Data {
    private static final List<Customer> customers = new ArrayList<>();
    static{
        for(int i = 0; i < 10; ++i){
            customers.add(new Customer("张三" + i, i, 1.7 + i));
        }
    }
    public static List<Customer> getCustomers(){
        return customers;
    }
    public static void add(Customer customer){
        customers.add(customer);
    }
}

但是 我们实现的这个项目有一个明显的缺点 即所展示的页面和地址栏没有互相对应 比如:以下界面展示的为list地址对应的画面 地址栏却为save
在这里插入图片描述
重定向就可以很好的解决这个问题

6.重定向

所谓重定向 其实就是服务器响应客户端提醒其再次发送一个新的任意的url
并且重定向操作的响应码为302

  • 响应由响应码(表示不同的响应操作 302表示重定向操作 而200表示正常响应)+响应头(包含content-type、location(用于指定客户端再次发送请求时新的url))
  • 重定向流程
    在这里插入图片描述
  • 我们就可以利用重定向来规避页面、地址栏不对应的弊端

SaveServlet 其中 利用sendRedirect完成了重定向操作 而且参数包含了context 原因在于这个参数指定了客户端重新发送的请求 而客户端发送的请求会拼接到IP:端口号之后

package com.mj.servlet;

import com.mj.bean.Customer;
import com.mj.bean.Data;

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("/save")
public class SaveServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 设置请求类型
        request.setCharacterEncoding("UTF-8");
        // 接收请求参数
        String name = request.getParameter("name");
        String age = request.getParameter("age");
        String height = request.getParameter("height");
        // 接着我们要将请求参数封装为一个bean类
        Customer customer = new Customer(name, Integer.valueOf(age), Double.valueOf(height));
        Data.add(customer);
        response.sendRedirect("/crm3/list");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
}

7.转发vs重定向

  • 转发:request.getRequestDispatcher("/路径").forward(request, response);
    • 只能转发到同一个Context下 路径中不用包含ContextPath
    • 客户端只发送了一次请求
    • 浏览器地址栏url不会改变
    • 转发的操作只有服务器完成
  • 重定向:response.sendRedirect("/路径");
    • 可以重定向到任意的url 如果重定向到同一个Context下 路径需要包含ContextPath
    • 浏览器发送了两次请求
    • 浏览器地址栏url会改变
    • 重定向操作由服务器+客户端配合完成
  • 14
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

axihaihai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值