【Java从零到架构师第二季】【04】EL表达式&JSTL标签库_转发&重定向_Servlet与JSP的对比及合作_路径


持续学习&持续更新中…

学习态度:守破离


EL表达式、JSTL标签库

什么是EL表达式、JSTL标签库

在这里插入图片描述

下载JSTL核心标签库

在这里插入图片描述

EL和JSTL实例

引入JSTL标签:

<%@ page language="java" contentType="text/html; charset=UTF-8"  pageEncoding="UTF-8"%>

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="f" uri="http://java.sun.com/jsp/jstl/fmt" %>

使用:

    <c:if test="${not empty customer}">
        <%-- 隐藏域 --%>
        <input type="hidden" name="id" value="${customer.id}">
    </c:if>
	<c:choose>
	    <c:when test="${empty user.photo}">
	        <img src="${ctx}/asset/admin/img/noimage.png" alt="">
	    </c:when>
	    <c:otherwise>
	        <img src="${ctx}/${user.photo}" alt="">
	    </c:otherwise>
	</c:choose>
	<td><f:formatDate pattern="yyyy-MM-dd" value="${education.beginDay}"/></td>
	<td><f:formatDate pattern="yyyy-MM-dd" value="${education.endDay}"/></td>
	<input type="date" name="birthday" value='<f:formatDate pattern="yyyy-MM-dd" value="${user.birthday}"/>'>
<c:forEach var="education" items="${educations}">
	<td>${education.typeString}</td>
	<td>${education.intro}</td>
</c:forEach>

利用EL表达式从JSP域对象中获取数据

域对象:作用域对象

在这里插入图片描述

在这里插入图片描述

四大scope:

<input type="text" value="${pageScope.name}">
<input type="text" value="${requestScope.name}">
<input type="text" value="${sessionScope.name}">
<input type="text" value="${applicationScope.name}">

对应的Java代码:

<% pageContext.getAttribute("name"); %>
<% request.getAttribute("name"); %>
<% session.getAttribute("name"); %>
<% application.getAttribute("name"); %>

EL表达式获取请求参数:

<input type="text" value="${param.name}">

对应的Java代码:

<% request.getParameter("name"); %>

注意

如果不写scope的话,那么:

  • EL表达式先去pageContext中取,如果取不到。
  • EL表达式然后去request中取
    1. ${user} == request.getAttribute("user")
    2. ${user.name} == request.getAttribute("user").getName()
  • request中取不到的话,就去session中去取
    1. ${user} == session.getAttribute("user")
    2. ${user.name} == session.getAttribute("user").getName()
  • session中取不到的话,就去application中去取

EL表达式中的${obj.property},property指的是属性,而不是obj这个对象所对应类的某个成员变量

举例:

    private Integer type;
    public Integer getType() {
    
    
        return type;
    }
    public void setType(Integer type) {
    
    
        this.type = type;
    }
    public String getTypeString() {
    
    
        String typeString = "其他";
        switch (type) {
    
    
            case 1:
                typeString = "小学";
                break;
            case 2:
                typeString = "初中";
                break;
            case 3:
                typeString = "高中";
                break;
            case 4:
                typeString = "中专";
                break;
            case 5:
                typeString = "大专";
                break;
            case 6:
                typeString = "本科";
                break;
            case 7:
                typeString = "硕士";
                break;
            case 8:
                typeString = "博士";
                break;
        }
        return typeString;
    }

<td>${education.typeString}</td>

转发(forward)

因为转发并不会响应浏览器,所以,转发后,浏览器地址栏中的url不会改变(浏览器是不知道发生了什么的),依然会是首次请求的url。又由于一次请求并没有结束,所以转发需要使用request进行操作。

转发只能在同一个项目(context)下进行,并且是由服务器来完成操作的,因此转发时使用相对路径即可,不用加context

转发时的相对路径是相对于此时浏览器中的路径的。

转发是同一次完整的请求,一次请求就是一个request,一个request就像下图那样,当把绿色或橙色这个请求和响应流程结束后,它才会消逝。

一个request可以被转发多次,但是每次都会创建新的request。具体看下图:

在这里插入图片描述

在这里插入图片描述

重定向(redirect)

重定向要先给浏览器一次响应,因此要使用response操作。

重定向的path是交给浏览器去重新请求的。因此需要给浏览器说明context path

重定向可以让浏览器重定向至http://host:port/context/...这种完整路径,也可以让其重定向至/context/...这种不完整路径(一个context就代表一个项目),如果给浏览器的是完整路径,那么浏览器就会直接请求该完整路径;如果给浏览器是不完整路径,那么浏览器默认会在该台服务器上去寻找该项目,也就是说,浏览器会在不完整路径之前拼接上http://host:post/

重定向的路径是由客户端(浏览器)发出的新的请求,凡是由客户端发出的新的请求(包括项目中jsp、html页面中的表单标签的action),当客户端发请求时,发现拿到的path是以/开头的,客户端只会通过http://host:port/找到该台Tomcat服务器,并不能知道要访问服务器下的哪个项目,因此需要告诉浏览器context path,向浏览器说明清楚需要访问哪一个项目。

在这里插入图片描述

转发与重定向

在这里插入图片描述

  • 转发:由服务器完成操作,只需要告诉相对路径(相对于浏览器此时地址栏中的URL)

  • 重定向:由客户端(浏览器)完成操作,需要告诉绝对路径或者需要告诉context path(比如HTML中的URL)

利用Servlet+JSP实现一个简单的客户关系管理系统

简述:首先访问用户列表页面,然后,用户可以点击添加,点击添加后跳转至添加用户界面,添加完成以后,点击保存,再次出现用户列表页面。

实现效果图

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

具体代码实现

Customer
public class Customer {
    
    

    private String name;
    private Integer age;
    private Float tall;

    public Customer(String name, Integer age, Float tall) {
    
    
        this.name = name;
        this.age = age;
        this.tall = tall;
    }

    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 Float getTall() {
    
    
        return tall;
    }

    public void setTall(Float tall) {
    
    
        this.tall = tall;
    }

    @Override
    public String toString() {
    
    
        return "Customer{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", tall=" + tall +
                '}';
    }
    
}
一些代码规范
  • 编写JavaBean时,字段使用对象类型能够很好的表达值不存在这个概念。因此建议使用对象类型,不建议使用基本类型。

在Java后台开发中,编写JavaBean时推荐将基本数据类型写为包装类型。
    究其原因是因为:
        如果使用对象类型的话,当某field为null时(某字段不存在数据时)也可以直接使用该字段。
        而使用基本数据类型的话,则无法来表述某个字段为null的场景。
比如有个温度字段,该如何表示温度不存在的情况?
就算可以使用一些规范来约定,也会很麻烦,不如直接使用包装类型

DataManage
public final class DataManage {
    
    

    private DataManage() {
    
    
    }

    private static final List<Customer> list = new ArrayList<>();

    static {
    
    
        for (int i = 0; i < 10; i++) {
    
    
            list.add(new Customer("张三" + i, 18 + i, (((i & 1) == 1)) ? 1.7f + i : 1.6f + i));
        }
    }

    public static void addCustomer(String name, Integer age, Float tall) {
    
    
        list.add(new Customer(name, age, tall));
    }

    public static List<Customer> getCustomers() {
    
    
        return list;
    }

}
UserListServlet

@WebServlet("/userlist")
public class UserListServlet extends HttpServlet {
    
    

    private static final String JSP_USERLIST = "pages/userlist.jsp";

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
    
        // 获取数据
        List<Customer> customers = DataManage.getCustomers();
        
        request.setAttribute("customers", customers);
        // 请求转发时是由服务器操作的,知道是在哪个项目中,因此写相对路径即可
        request.getRequestDispatcher(JSP_USERLIST).forward(request, response);
    }

}
userlist.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>user list</title>
    <style>
        th, td {
      
      
            border: 1px solid black;
        }
    </style>
</head>
<body>

<!-- a标签跳转至某个页面也是由浏览器执行的,因此需要写context path -->
<a href="/five_crms/pages/adduser.jsp">添加</a>

<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.tall}</td>
        </tr>
    </c:forEach>

    </tbody>
</table>

</body>
</html>
adduser.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>add user</title>
</head>
<body>

<!-- form表单是由浏览器进行请求的,只知道在哪台服务器上,因此需要写上项目context -->
<form action="/five_crms/adduser" method="post">
    <div>姓名:<input type="text" name="username"></div>
    <div>年龄:<input type="text" name="userage"></div>
    <div>身高:<input type="text" name="usertall"></div>
    <br>
    <br>
    <br>
    <div>
        <button type="submit">保存</button>
    </div>
</form>
</body>
</html>
AddUserServlet
@WebServlet("/adduser")
public class AddUserServlet extends HttpServlet {
    
    
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
    

        request.setCharacterEncoding("utf-8");

        DataManage.getCustomers().add(new Customer(
                request.getParameter("username"),
                Integer.valueOf(request.getParameter("userage")),
                Float.valueOf(request.getParameter("usertall"))
        ));

		// 请求重定向是通知浏览器对某个路径进行重新请求,浏览器只知道在哪台服务器上,因此需要写上项目context
        response.sendRedirect("/five_crms/userlist");
        // 第二种方式实现重定向 不推荐使用
        // response.setStatus(302); // 状态码
        // response.setHeader("Location","/five_crms/userlist"); // 响应头Location

    }
}

Servlet与JSP的对比及合作

Servlet处理请求的常见过程及弊端

在这里插入图片描述

弊端:

  • Servlet中有大量的字符串拼接代码

  • Servlet即需要和数据打交道,又需要和界面打交道。

  • 如果响应给客户端的HTML中需要引入CSS文件呢?这时也只能字符串拼接。

JSP处理请求的常见过程及弊端

在这里插入图片描述

弊端:

  • Java和HTML混合在一起,可读性特别差

  • 前后端不分离,难以维护

  • 依然没有从根本上解决字符串拼接问题

  • 变得比直接使用Servlet书写更麻烦,更难以维护(Servlet只需要拼接字符串)。

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

在这里插入图片描述

优势:

  • Servlet负责调度,负责业务逻辑的控制

  • JSP负责页面显示(使用JSTL、EL)。

  • 可读性高、易维护、条理清晰。

Servlet+JSP合作开发

目标
  • Java文件中不拼接任何一句HTML,只写Java代码。(让Servlet只控制业务)

  • JSP中不编写一句Java代码,只写标签。

实现

UserListServlet.java:

@WebServlet("/userlist")
public class UserListServlet extends HttpServlet {
    
    

    private static final String JSP_USERLIST = "pages/userlist.jsp";

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
    
    	// 如果需要从浏览器读取信息的话需要设置请求的字符集编码
        // 设置字符集编码
        // request.setCharacterEncoding("UTF-8");
        // 响应的字符编码是没有必要设置的,因为Servlet只负责业务控制,和数据打交道,不负责展示
        // response.setContentType("text/html;charset=UTF-8");

        // 获取客户数据
        List<Customer> customers = getDataList();

        // 将客户数据存储到request中
        request.setAttribute("customers", customers);

        // 转发到userlist.jsp页面进行数据展示
        // 转发时,路径默认是从该项目的webapp下开始寻找的
        /* 
        转发只能在同一个Context中进行转发,也就是说Serlet只会在同一个项目中进行转发。
        既然大家都在同一个项目下,因此这儿写相对路径即可。
        该相对路径前面会自动被拼接上context path
        */
        request.getRequestDispatcher(JSP_USERLIST).forward(request, response);
    }

    /*实际开发中获取数据肯定是通过数据库来获取的*/
    private List<Customer> getDataList() {
    
    
        List<Customer> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
    
    
            list.add(new Customer("张三" + i, 18 + i, (((i & 1) == 1)) ? 1.7f + i : 1.6f + i));
        }
        return list;
    }
}

userlist.jsp:

<%--  page指令下的contentType相当于response.setContentType  --%>
<%--  因为JSP的本质就是Servlet  --%>
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%--  因为是core库,所以prefix简写为c即可  --%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>user list</title>
    <style>
        th, td {
      
      
            border: 1px solid black;
        }
    </style>
</head>
<body>

<a href="">添加</a>

<table>
    <thead>
    <tr>
        <th>姓名</th>
        <th>年龄</th>
        <th>身高</th>
    </tr>
    </thead>
    <tbody>

    <%--
            items中的${argument}相当于
            通过request.getAttribute("argument")
            得到request的attribute中的对象

			${customer.name}相当于customer.getName()
    --%>
    <c:forEach items="${customers}" var="customer">
        <tr>
            <td>${customer.name}</td>
            <td>${customer.age}</td>
            <td>${customer.tall}</td>
        </tr>
    </c:forEach>

    </tbody>
</table>

</body>
</html>

路径

http://host:port/context对应项目中的webapp目录

相对路径

  • http://host:port/context/对应webapp目录

  • @WebServlet("/list*"),此时服务器处于根目录(/ 对应 webapp目录)

  • http://localhost:8080/crms/customer/list对应webapp/customer目录下的list

  • @WebServlet("/customer/list") 此时服务器处于根目录下的customer目录(/customer 对应 webapp/customer目录)


  • 转发时,相对的是浏览器此时地址栏中的路径

  • 使用forward转发时,getRequestDispatcher中只能填写相对路径。有两种方式:

		// ../的意思是跳转至webapp目录下(相对于浏览器此时地址栏的路径)
		// 假设此时浏览器地址栏:http://localhost:8080/lp_resume/education/admin
		// 此时处于根目录下的education目录,那么../就会跳转到根目录
		req.getRequestDispatcher("../WEB-INF/error.jsp").forward(req, resp);
		
		// 使用/代表根目录(webapp)
		req.getRequestDispatcher("/WEB-INF/error.jsp").forward(req, resp);

绝对路径

  • /context_path开头的是绝对路径

  • 直接带上盘符的路径和直接带上context path的路径。

  • 重定向只能使用绝对路径,两种方式:

		// 由于context path有可能会改变,因此使用动态context path
		resp.sendRedirect(req.getContextPath() + "/company/admin");
		
		resp.sendRedirect("http://localhost:8080/lp_resume/company/admin");

JSP文件中路径的使用

  • JavaWeb开发中,jsp和html中的路径应该使用以/context_path开头的绝对路径

  • 由于context path有可能会更改,所以应该使用动态的context path

在这里插入图片描述

  • include指令中的路径使用相对路径

include指令的本质是copy

<%@ include file="common/head.jsp" %>

注意和一些细节

  • 表单中"name"存在时,request.getParameter("name")在用户没有输入的情况下会返回空字符串

  • name不存在时,request.getParameter("name")为null。


  • JSP的本质是Servlet,JSP中的JSTL和EL表达式同样也会被编译为Servlet,最终都是由Servlet输出一个HTML网页响应给浏览器的。

  • EL表达式中的Bean对象默认都会在Java中调用toString()转化为字符串发送给浏览器,因此我们可以在BaseBean中添加一个getJson()方法,用于将一个JavaBean对象转为JSON,然后发送给浏览器,浏览器中的JSON可以直接被解析为JS对象使用。

    @JsonIgnore
    public String getJson() {
    
    
        try {
    
    
            return Jsons.objectMapper().writeValueAsString(this).replace("\"", "'");
        } catch (JsonProcessingException e) {
    
    
            return null;
        }
    }
<button type="button" onclick="edit(${company.json})"><span>编辑</span></button>
    function add() {
    
    
        $addSaveForm[0].reset()
        addFormBox.modal()
    }

    function edit(json) {
    
    
        add()
        for (const k in json) {
    
    
            $addSaveForm.find('[name=' + k + ']').val(json[k])
        }
    }

参考

李明杰: Java从0到架构师②JavaEE技术基石.


本文完,感谢您的关注支持!


猜你喜欢

转载自blog.csdn.net/weixin_44018671/article/details/120708986