JavaEE开发-第一节 Servlet与JSP

Servlet基本使用

  1. Servlet是Server Applet的简称,译为“小型的服务程序”,用于响应客户端的请求
  2. 一般的使用要素
    1. 继承java.servlet.http.HttpServlet,实现doGet、doPost或者service方法
    2. 通过request对象获取客户端的请求数据:request.getParameter()
    3. 通过response对象给客户端返回响应: response.getWriter().write()
    4. 通过注解@WebServlet设置Servlet对应的请求路径
  3. 乱码问题解决
    1. 客户端的请求数据乱码

       //按照UTF8编码解析客户达发送过来的数据
       request.setCharacterEncoding("UTF-8");
      
    2. 服务器的响应数据乱码

       //设置响应数据的编码
       response.setContentType("text/plain;charset=UTF-8");
      
      1. 其中text/plain是数据的MINEType,根据实际情况而定
      2. 更多的MINEType可以参考Tomcat安装目录下的conf文件夹下/web.xml文件中可以查看
  4. 一些细节
    1. 通过response拿到的输出流对象(比如getWriter),不需要程序员调用close去关闭
      1. 在语法基础IO流中讲过,流一般创建了,需要用close销毁,这里是获取,不是创建所以不用销毁
    2. 默认情况下,一个Servlet类,只会被服务器创建一个实例对象,而且是在第一次处理客户端请求才创建实例
      1. 注意:Servlet并没有设计成单例模式!!!
    3. 建议:不要在Servlet中定义可写(writable)的成员变量,会引发线程安全问题
    4. HTTP请求的默认端口号是80,所以,如果Tomcat服务器的端口号设置为80,那么
      1. localhost/path等价于localhost:80/path(仅仅是HTTP请求才会自动加上80)
      2. 也就是说如果Tomcat服务器的端口号设置的不是80,那么HTTP请求的时候就必须是域名(IP地址):+端口号进行访问
      3. 注意:这里的80不等于8080
      4. HTTPS请求的默认添加端口号是443
  5. 代码举例:

     //TestServlet类
     package com.example.HelloWorld;
     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("/login")
     //设置多个请求路径
     //@WebServlet({"/login","/test"})
     public class TestServlet extends HttpServlet {
         //可以从构造方法中看出,n个请求发送过来,TestServlet对象仅仅被创建一次
         public TestServlet(){
             //java中打印一个对象时,输出:类名@哈希值,每个对象都有唯一的哈希值,如果2个对象打印的值一样,说明2个对象是同一个对象
             System.out.println(this + "server");
         }
         /**
          * 客户端发送一个http请求,首先会调用这个service方法
          * */
         @Override
         protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
             //父类的service方法内部已经做好了分发,会判断当前客户端的请求是get还是post,然后分别调用doGet、doPost
             super.service(req, resp);
             System.out.println("------service-----------");
             //java中打印一个对象时,输出:类名@哈希值,每个对象都有唯一的哈希值,如果2个对象打印的值一样,说明2个对象是同一个对象
             System.out.println(this + "server");
         }
        
         //处理get请求
         @Override
         protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
             System.out.println("------doget-----------");
             //统一函数处理
             doPost(req,resp);
         }
         //处理post请求
         /**
          * HttpServletRequest req: 请求,用来获取客户端发送的数据
          * HttpServletResponse resp:响应,用来给客户端返回数据
          * */
         @Override
         protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
             System.out.println("-------dopost---------");
             //获取客户端发送的数据(请求参数)
             //按照UTF8编码解析客户达发送过来的数据
             req.setCharacterEncoding("UTF-8");
             String username = req.getParameter("username");
             String password = req.getParameter("password");
             System.out.println(username + "---" + password);
             //设置响应数据的MINE Type和数据编码
             resp.setContentType("text/plain;charset=UTF-8");
             //2. 判断
             if("123".equals(username) && "456".equals(password)){
                 System.out.println("登录成功");
                 resp.getWriter().write("Login Success!");
             }else {
                 System.out.println("登录失败");
                 resp.getWriter().write("Login Error!");
             }
         }
     }
    

用Servlet模拟一个前后端项目

  1. web文件下新建一个login.html、login.css2个文件,login.html代码如下

     <!DOCTYPE html>
     <html lang="en">
     <head>
         <meta charset="UTF-8">
         <title>CRM-登录</title>
     </head>
     <body>
     <!-- context path -->
     <!--<form action="/crm/test.jsp" method="post">-->
     <form action="/crm/login" method="post">
         <div>用户名 <input type="text" name="username"></div>
         <div>密码 <input type="text" name="password"></div>
         <div><button type="submit">登录</button></div>
     </form>
        
     </body>
     </html>
    
  2. java文件夹下,com.zh.crm包名下

    1. 新建一个bean文件夹,用于存放数据的模型,新建一个Customer模型类
    2. 新建一个servlet,用来存放HttpServlet的子类对象,新建一个LoginServlet类
    3. LoginServlet代码如下:

       package com.zh.crm.servlet;
       import com.zh.crm.bean.Customer;
       import javax.servlet.*;
       import javax.servlet.http.*;
       import javax.servlet.annotation.*;
       import java.io.IOException;
       import java.io.PrintWriter;
       import java.util.ArrayList;
       import java.util.List;
              
       @WebServlet("/login")
       public class LoginServlet extends HttpServlet {
           @Override
           protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
               System.out.println("------doget-----------");
               //统一函数处理
               doPost(request,response);
           }
              
           @Override
           protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
               System.out.println("-------dopost---------");
               //获取客户端发送的数据(请求参数)
               //按照UTF8编码解析客户达发送过来的数据
               request.setCharacterEncoding("UTF-8");
               String username = request.getParameter("username");
               String password = request.getParameter("password");
               System.out.println(username + "---" + password);
               //设置响应数据的MINE Type和数据编码
               response.setContentType("text/html;charset=UTF-8");
               // 4.拿到输出流
               PrintWriter out = response.getWriter();
               // 5.判断
               if ("123".equals(username) && "123".equals(password)) {
                   success(out);
               } else {
                   failed(out);
               }
           }
           //获取客户,模拟数据库
           private List<Customer> getCustomers() {
               List<Customer> customers = new ArrayList<>();
               for (int i = 0; i< 5; i++) {
                   customers.add(new Customer("张三" + i, "432423" + i, ((i & 1) == 1) ? "男" : "女"));
               }
               return customers;
           }
           //手动拼接字符串
           private void success(PrintWriter out) {
               out.write("<html>");
               out.write("<head>");
               //引用css文件
               out.write("<link rel=\"stylesheet\" href=\"http://localhost:8080/crm/login.css\">");
               out.write("</head>");
               out.write("<body>");
               out.write("<h1 style=\"color: blue; border: 1px solid black;\">登录成功</h1>");
               out.write("<table>");
               out.write("<thead>");
               out.write("<tr>");
               out.write("<th>姓名</th>");
               out.write("<th>电话</th>");
               out.write("<th>性别</th>");
               out.write("</tr>");
               out.write("</thead>");
               out.write("<tbody>");
               //获取客户信息,拼接标签
               List<Customer> customers = getCustomers();
               for (Customer customer : customers) {
                   out.write("<tr>");
                   out.write("<td>" + customer.getName() + "</td>");
                   out.write("<td>" + customer.getPhone() + "</td>");
                   out.write("<td>" + customer.getSex() + "</td>");
                   out.write("</tr>");
               }
              
               out.write("</tbody>");
               out.write("</table>");
               out.write("</body>");
               out.write("</html>");
           }
              
           private void failed(PrintWriter out) {
               out.write("<h1 style=\"color: red; border: 1px solid black;\">登录失败</h1>");
               out.write("<ul>");
               out.write("<a href=\"http://localhost:8080/crm/login.html\">重新登录</a>");
               out.write("</ul>");
           }
       }
      
    4. Customer代码如下:

       package com.zh.crm.bean;
       //客户
       public class Customer {
           private String name;
           private String phone;
           private String sex;
           public Customer() {}
           public Customer(String name, String phone, String sex) {
               this.name = name;
               this.phone = phone;
               this.sex = sex;
           }
           public String getName() {
               return name;
           }
           public void setName(String name) {
               this.name = name;
           }
           public String getPhone() {
               return phone;
           }
           public void setPhone(String phone) {
               this.phone = phone;
           }
           public String getSex() {
               return sex;
           }
           public void setSex(String sex) {
               this.sex = sex;
           }
           // Alt + Insert
       }
      

问题总结

  1. 登录之后显是的网页是用后端代码一句一句拼接的,比较繁琐
  2. 那么有没有简单的方法呢? —–JSP

JSP基本使用

  1. JSP是JavaServer Pages的简称,是一种动态网页技术标准
  2. 指令

     <%@ page%>: 配置当前页面信息
     <%@ include%>: 包含其他页面
     <%@ taglib%>: 导入标签库
    
  3. 输出

     <%= 需要输出的内容%>
     等价于out.print(需要输出的内容)
     response.getWriter().print(需要输出的内容)
    
  4. 嵌入Java代码

     <%Java代码%>
    
  5. 注释

     <%--注释内容--%>
     HTML/CSS/JS注释照常使用
    
  6. 声明

     <%!声明成员变量、方法%>
    

JSP实现前后端项目

  1. 在web文件夹下新建一个test.jsp
  2. 将login.html的form表单的aciton修改成:action="/crm/test.jsp"
  3. test.jsp 代码如下

     <%@ page import="java.util.ArrayList" %>
     <%@ page import="java.util.List" %>
     <%@ page import="com.zh.crm.bean.Customer" %>
     <%@ page contentType="text/html;charset=UTF-8" language="java" %>
     <html>
     <head>
         <meta charset="UTF-8">
         <title>CRM-登录</title>
         <link rel="stylesheet" href="http://localhost:8080/crm/login.css">
     </head>
     <body>
     <%--
         jsp就是一个server类,就是一个继承自HttpServlet类的子类
         这里面可以定义成员变量、方法
         可以写java代码,java代码就是相当于在doGet、doPost的方法中写代码
         jsp中的所有标签都会通过response.getWriter().write()方法,从上到下响应给客户端
     --%>
         <%!
             //定义成员变量,
             //private  int other = 10;
             //定义成员方法,模拟数据
             private List<Customer> getCustomers() {
                 List<Customer> customers = new ArrayList<>();
                 for (int i = 0; i< 5; i++) {
                     customers.add(new Customer("张三" + i, "432423" + i, ((i & 1) == 1) ? "男" : "女"));
                 }
                 return customers;
             }
         %>
        
        
         <%
             //这个就相当于在doGet、doPost的方法中写代码
             // 1.设置请求数据的编码
             request.setCharacterEncoding("UTF-8");
             // 2.获取请求参数
             String username = request.getParameter("username");
             String password = request.getParameter("password");
             // 3.先设置响应的内容类型(MIMEType + 数据编码)
             response.setContentType("text/html;charset=UTF-8");
             // 4.拿到输出流
             // 5.判断,成功
             if ("123".equals(username) && "123".equals(password)) {
         %>
             <h1 style="color: blue; border: 1px solid black;">登录成功</h1>
             <table>
                 <thead>
                     <tr>
                         <th>姓名</th>
                         <th>电话</th>
                         <th>性别</th>
                     </tr>
                 </thead>
                 <tbody>
                 <%
                     List<Customer> customers = getCustomers();
                     for (Customer customer : customers) {
                 %>
                     <tr>
                         <td><%= customer.getName()%></td>
                         <td><%= customer.getPhone()%></td>
                         <td><%= customer.getSex()%></td>
                     </tr>
                 <%
                         }
                 %>
                 </tbody>
             </table>
             <%
                 }else {
             %>
             <h1 style="color: #ff0000; border: 1px solid #000000;">登录失败</h1>
             <ul><a href="http://localhost:8080/crm/login.html">重新登录</a></ul>
             <%
                 }
             %>
     </body>
     </html>
    
  4. 总结
    1. jsp本质就是一个servlet,就是一个继承自HttpServlet类的子类,就相当于我们手动写的LoginServlet类
    2. 这里面可以定义成员变量、方法
    3. 可以写java代码,java代码就是相当于在service的方法中写代码
    4. jsp中的所有非java代码(H5标签)本质都是通过response.getWriter().write()方法,从上到下响应给客户端
  5. 查看编译后的代码
    1. 运行项目后,查看控制台,点击Tomcat Catalina Log
    2. 找到下面一句:

       CATALINA_BASE:[/Users/mac/Library/Caches/JetBrains/IntelliJIdea2020.3/tomcat/c8454ae9-56f7-49f4-af61-eaf70a2a9796]
      
    3. 然后进入到该目录下,在/work/Catalina/localhost/crm/org/apache/jsp/文件夹下可以看到test_jsp.java文件
    4. 打开开文件,可以看到
      1. test_jsp这个类继承HttpJspBase
      2. 双击shift搜索HttpJspBase,点击tomcat源码进入HttpJspBase查看

         public abstract class HttpJspBase extends HttpServlet implements HttpJspPage {}
        
        1. HttpJspBase继承自HttpServlet
      3. jsp所有的代码都是在_jspService方法中执行
      4. 所有的标签都是通过out.write()响应给客户端

总结

  1. 尽管JSP简化了java原生拼接标签字符串的麻烦,但是一个jsp文件也有明显的缺点.
  2. Java代码和HTML代码混合在一起,导致可读性差、难以维护
  3. 整个JSP文件基本得由Java后台工程师来完成,前端工程师根本无法维护
  4. 在很久以前,有些公司是需要前端后台一起开发JSP。现在比较流行前后端分离
  5. 从上面看出,对于有web的项目单独使用Servlet,单独使用JSP都是不行的,那么,可不可以这样呢? Java代码部分用Servlet,HTML部分用JSP呢?

EL表达式、JSTL标签库

  1. EL是Expression Launguage的简称

     ${obj.property}、${obj["property"]}、${obj[propertyVar]}
     empty、not empty
    
  2. JSTL是JSP Standard Tag Library的简称,译为”JSP标准标签库“,由Apache的Jakarta小组维护
  3. EL表达式、JSTL标签库都可以简化JSP代码

下载和使用JSTL核心标签库

  1. 下载地址:http://tomcat.apache.org/download-taglibs.cgi
    1. 如果使用其核心标签库,只需要下载2个jar包即可
      1. taglibs-standard-impl-1.2.5.jar、taglibs-standard-spec-1.2.5.jar
  2. IDEA如何手动导入第三方jar包(javaEE项目):
    1. 在WEB-INF文件夹下新建一个lib文件夹
    2. 将上面2个jar包复制到该目录下
    3. 右击选择”Add das Library”->Level选择Module Library,点击OK
  3. 在jsp文件中,使用taglib指令导入JSTL核心标签库

     <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    
  4. 常用标签

     <c:if test="条件">
     <c:for Each items="集合" var="元素" varStatus="循环相关的信息">
     <c:choose>、<c:when test="条件">、<c:oth erwise>
    
  5. 代码举例
    1. 将LoginServlet中得doPost代码改造如下

       @Override
       protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
           System.out.println("-------dopost---------");
           //获取客户端发送的数据(请求参数)
           //按照UTF8编码解析客户达发送过来的数据
           request.setCharacterEncoding("UTF-8");
           String username = request.getParameter("username");
           String password = request.getParameter("password");
           System.out.println(username + "---" + password);
           //设置响应数据的MINE Type和数据编码
           response.setContentType("text/html;charset=UTF-8");
           // 5.判断
           if ("123".equals(username) && "123".equals(password)) {
               //获取客户信息,拼接标签
               List<Customer> customers = getCustomers();
               // 将客户数据存储到request中
               request.setAttribute("customers", customers);
               //request.getAttribute("customers");
               // 转发到login.jsp页面进行数据展示,即转发给另外一个servlet(jsp的本质是servlet)
               request.getRequestDispatcher("/login.jsp").forward(request, response);
               //equestDispatcher dispatcher = request.getRequestDispatcher("/page/list.jsp");
               //dispatcher.forward(request, response);
           }
       }
      
    2. 在web文件夹下新建一个login.jsp(要导入标签库,步骤见2、3)

       <%--本质就是response.setContentType("text/html;charset=UTF-8");--%>
       <%@ page contentType="text/html;charset=UTF-8" language="java" %>
       <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
       <html>
       <head>
           <meta charset="UTF-8">
           <title>CRM-登录</title>
           <link rel="stylesheet" href="http://localhost:8080/crm/login.css">
       </head>
       <body>
       <h1 style="color: blue; border: 1px solid black;">登录成功</h1>
       <table>
           <thead>
           <tr>
               <th>姓名</th>
               <th>电话</th>
               <th>性别</th>
           </tr>
           </thead>
           <tbody>
       <%--
       forEach中的 ${customers} :是EL表达式,里面存放的是key值,会到request中取value
       等价于:request.getAttribute("customers");
       --%>
           <c:forEach items="${customers}" var="customer" varStatus="s">
               <tr>
       <%--
       customer.name:这里的name不是成员变量,而是属性名
       拿到属性名,会自动去找该属性名的get方法,本质是customer.getName()
       s:可以获取遍历中的索引,s.index/s.begin 等等
       --%>        
                          
                   <%-- <td>${s.index}</td> --%> 
                   <td>${customer.name}</td>
                   <td>${customer.phone}</td>
                   <td>${customer.sex}</td>
               </tr>
           </c:forEach>
              
           </tbody>
       </table>
       </body>
       </html>
      
    3. 流程分析

      1. form表格,点击登录,发送post请求,调用LoginServlet的doPost

         //login.html核心代码
         <form action="/crm/login2" method="post">
        
             <div>用户名 <input type="text" name="username"></div>
             <div>密码 <input type="text" name="password"></div>
             <div><button type="submit">登录</button></div>
         </form>
        
      2. LoginServlet获取用户数据,将数据转发给jsp(另一个Servlet,jsp的本质是servlet)
      3. jsp将结果响应给客户端

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

  1. 本质就是一个MVC的开发模式
    1. C(Controller):控制器(Servlet)
    2. M(Model):数据
    3. V(View):页面展示(JSP)

图1

转发(forward)

  1. 同一个Context(项目)中进行请求转发
  2. 一个请求只能在同一个项目的Servlet(JSP)之间转发

图1

转发链条(重点,注意理解!!!)

  1. 在同一次请求中,可以转发多次,形成一个转发链条。在一个转发链条上
    1. 可以通过request.setAttribute、request.getAttribute来共享数据
    2. 每一次转发都会创建一个新的request对象,用成员变量request指向前一个request对象
      1. 每一个新的request对象,都有一个成员变量request,这个request指向前request一个对象
  2. 在转发链条上,所有的attribute都存储在头部的request对象
    1. 这就是为什么转发链条中的所有request对象获取的attribute值都是一样的原因,因为链条中所有通过attribute存储的数据,都存放在第一个request中
  3. 代码举例:
    1. 新建3个Servelt

       //Test1Servlet
       @WebServlet("/test1")
       public class Test1Servlet extends HttpServlet {
           protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
               // RequestFacade
               //设置数据
               request.setAttribute("name", "jack");
               System.out.println("Test1Servlet_" + request);
               //下句打断点,断点1
               //转发到Test2Servlet
               request.getRequestDispatcher("/test2").forward(request, response);
           }
       }
              
       //Test2Servlet
       @WebServlet("/test2")
       public class Test2Servlet extends HttpServlet {
              
           protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
               System.out.println("Test2Servlet_" + request);
               //第二个断点
               //转发到Test3Servlet
               request.getRequestDispatcher("/test3").forward(request, response);
           }
       }
       //Test3Servlet
       @WebServlet("/test3")
       public class Test3Servlet extends HttpServlet {
           protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
               //第三个断点处
               System.out.println("Test3Servlet_" + request);
           }
       }
      
    2. 然后浏览器输入:http://localhost:8080/crm/test1,点击回车
    3. 打印结果

       Test1Servlet_org.apache.catalina.connector.RequestFacade@6a18878a
       Test2Servlet_org.apache.catalina.core.ApplicationHttpRequest@57075213
       Test3Servlet_org.apache.catalina.core.ApplicationHttpRequest@a178cde
      
      1. 可以看到3个request对象不是同一个
    4. 打断点,可以看到下面的转发链条

      图1

重定向(redirect)

  1. 重定向:服务器通知客户端重新发送请求到新的任意URL地址

    图1

    1. 客户端发送网络请求到Context1,Context1响应状态码302,并设置响应头Location字段中的重定向的路径或者url,这里假设路径是Context2的路径
    2. 客户端发现服务器返回的状态码是302,则会到响应头的Location字段中获取新的地址
    3. 拿到新的地址向新地址发送服务请求到Context2,Context2返回200和响应数据
  2. 注意:302表达的意思就是告诉客户端,需要重定向
  3. 重新向可以到任意Context之间

转发vs重定向

  1. 转发代码:request.get RequestDispatcher("/路径").forward(request,response)
    1. 只能转发到同一个Context(项目), 路径中不用包含ContextPath(项目名称路径)
    2. 客户端只发了一次请求
    3. 浏览器地址栏的URL不会发生变化
    4. 转发的操作只由服务器完成
  2. 重定向代码:response.sendRedirect("/路径")
    1. 可以重定向到任意URL, 如果重定向到同一个Context下, 路径中需要包含ContextPath
    2. 客户端发了两次请求
    3. 浏览器地址栏的URL发生变化
    4. 重定向的操作由服务器+客户端配合完成
  3. 使用场景:
    1. 转发:在一个servlet中从数据库中读取到数据,然后转发给另外一个servlet(JSP)进行数据展示
    2. 重定向:在一个servlet中向数据库中写入完数据,然后让看客户端重定向到某个列表页面展示数据库更新的数据。

代码举例(用户列表维护)

  1. 需求:
    1. 第一个页面是用户列表,顶部有个添加按钮
    2. 点击添加进入添加页面,输入用户信息,点击保存重定向到用户列表页面
  2. 监听客户端进入用户列表页面请求ListServlet:http://localhost:8080/crm/list
    1. ListServlet

       package com.zh.crm.servlet;
       import com.zh.crm.Data;
       import com.zh.crm.bean.Customer;
       import javax.servlet.*;
       import javax.servlet.http.*;
       import javax.servlet.annotation.*;
       import java.io.IOException;
       import java.util.List;
              
       @WebServlet("/list")
       public class ListServlet extends HttpServlet {
           @Override
           protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
               doPost(request,response);
           }
              
           @Override
           protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
               // 获取客户数据
               List<Customer> customers = Data.getCustomers();
               // 将客户数据存储到request中
               request.setAttribute("customers", customers);
               request.getAttribute("customers");
               // 转发到list.jsp页面进行数据展示
               request.getRequestDispatcher("/page/list.jsp").forward(request, response);
           }
       }
      
    2. list.jsp

       <%--本质就是response.setContentType("text/html;charset=UTF-8");--%>
       <%@ page contentType="text/html;charset=UTF-8" language="java" %>
       <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
       <!DOCTYPE html>
       <html lang="zh">
       <head>
           <meta charset="UTF-8">
           <title>客户列表</title>
           <style>
               th, td {
                   border: 1px solid black;
               }
           </style>
       </head>
       <body>
       <a href="page/add.html">添加</a>
              
       <table>
           <thead>
           <tr>
               <th>姓名</th>
               <th>年龄</th>
               <th>身高</th>
           </tr>
           </thead>
           <tbody>
              
       <%--
       forEach中的 ${customers} :是EL表达式,里面存放的是key值,回到request中取value
       等价于:request.getAttribute("customers");
       --%>
           <c:forEach items="${customers}" var="customer" varStatus="s">
               <tr>
                   <td>${customer.name}</td>
                   <td>${customer.age}</td>
                   <td>${customer.height}</td>
               </tr>
           </c:forEach>
              
           </tbody>
       </table>
              
       </body>
       </html>
      
  3. 添加用户信息界面add.html

     <!DOCTYPE html>
     <html lang="zh">
     <head>
         <meta charset="UTF-8">
         <title>添加客户</title>
     </head>
     <body>
        
     <form action="/crm/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>
    
  4. 监听form表单的post请求SaveServlet

     package com.zh.crm.servlet;
     import com.zh.crm.Data;
     import com.zh.crm.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;
        
     @WebServlet("/save")
     public class SaveServlet 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 {
             request.setCharacterEncoding("UTF-8");
        
             // 获取请求参数
             String name = request.getParameter("name");
             String age = request.getParameter("age");
             String height = request.getParameter("height");
        
             // 转成Java Bean对象
             Customer customer = new Customer();
             customer.setName(name);
             customer.setAge(Integer.valueOf(age));
             customer.setHeight(Double.valueOf(height));
             Data.add(customer);
                
             // 重定向
             //重定向是告诉客户端,要重新发送一个url请求到服务器,相当于再次在浏览器地址框中输入了:http://localhost:8080/crm/list,并回车
             // 状态码:302,告诉客户端,需要重定向,然后客户端到响应头的Location字段中查找要重定向的新路径
             // response.setStatus(302);
             // 响应头Location:/crm/list
             // response.setHeader("Location", "/crm/list");
             //转发可以在不同项目之间,所以路径必须带上项目名/crm
             //sendRedirect方法就是上面2句的本质
             response.sendRedirect("/crm/list");
        
             //转发的话,浏览器的访问地址栏不会改变,客户端没有重新请求,而是服务端Servlet之间的转发
             //客户端并不知道服务端发生了什么事情
             //转发只能在同一个项目的Servlet(JSP)之间,因此路径不需要带上项目名称crm
             // request.getRequestDispatcher("/list").forward(request, response);
         }
     }
    
Table of Contents