天天看點

web-6-深入了解JSP:探索其核心概念和特性

作者:springboot葵花寶典

JSP

今日目标:

了解 JSP 及 JSP 原理

能在 JSP中使用 EL表達式 和 JSTL标簽

1,JSP 概述

JSP(全稱:Java Server Pages):Java 服務端頁面。是一種動态的網頁技術,其中既可以定義 HTML、JS、CSS等靜态内容,還可以定義 Java代碼的動态内容,也就是 JSP = HTML + Java。如下就是jsp代碼
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Hello JSP</title>
</head>
<body>
    <h1>你好 JSP</h1>
    <%
        System.out.println("你好 Java代碼");
    %>
</body>
</html>

           

在浏覽器位址欄輸入:http://localhost:8080/jsp/01_Hello_JSP.jsp 顯示結果為:

web-6-深入了解JSP:探索其核心概念和特性

思考:JSP 能做什麼呢?

JSP 作用:簡化開發,避免了在Servlet中直接輸出HTML标簽。
           

2,JSP 快速入門

接下來我們做一個簡單的快速入門代碼。

2.1 搭建環境

建立一個maven的 web 項目,項目結構如下:

web-6-深入了解JSP:探索其核心概念和特性

pom.xml 檔案内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zbbmeta</groupId>
    <artifactId>07_JSP</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>war</packaging>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>

    </dependencies>

</project>
           

2.2 導入 JSP 依賴

在 dependencies 标簽中導入 JSP 的依賴,如下

<!--        jsp-->
<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.2</version>
  <!--因為tomcat存在jsp的jar包,我們隻是在開發階段使用,索引scope選擇為provided-->
    <scope>provided</scope>
</dependency>


           

2.3 建立 jsp 頁面

在項目的 webapp 下建立jsp頁面

web-6-深入了解JSP:探索其核心概念和特性

通過上面方式建立一個名為 hello.jsp 的頁面。

2.4 編寫代碼

在 hello.jsp 頁面中書寫 HTML 标簽和 Java 代碼,如下

<%--
  Created by IntelliJ IDEA.
  User: zbb
  Date: 0029
  Time: 0:32
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Hello JSP</title>
</head>
<body>
    <h1>你好 JSP</h1>
    <%
        System.out.println("你好 Java代碼");
    %>
</body>
</html>
           

2.5 測試

啟動伺服器并在浏覽器位址欄輸入 http://localhost:8080/jsp/01-hello-jsp.jsp,我們可以在頁面上看到如下内容

web-6-深入了解JSP:探索其核心概念和特性

在 idea 的控制台看到輸出的 你好 Java代碼 内容。

3,JSP 原理

思考:為什麼還可以寫 Java 代碼呢?

因為JSP 本質上就是一個 Servlet
web-6-深入了解JSP:探索其核心概念和特性
  1. 浏覽器第一次通路 01-hello-jsp.jsp 頁面
  2. tomcat 會将 01-hello-jsp.jsp 轉換為名為 _01_002dhello_002djsp_jsp.java 的一個 Servlet
  3. tomcat 再将轉換的 servlet 編譯成位元組碼檔案 _01_002dhello_002djsp_jsp.class
  4. tomcat 會執行該位元組碼檔案,向外提供服務
web-6-深入了解JSP:探索其核心概念和特性
web-6-深入了解JSP:探索其核心概念和特性

打開 _01_002dhello_002djsp_jsp.java 檔案,來檢視裡面的代碼

public final class _01_002dhello_002djsp_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent,
                 org.apache.jasper.runtime.JspSourceImports {
           

可以看出其繼承 HttpJspBase 這個類,那我們在看該類的繼承關系。到資料中的找如下目錄: apache-tomcat-9.0.65-src\java\org\apache\jasper\runtime(可自行到Tomcat官網下載下傳src壓縮包) ,該目錄下就有 HttpJspBase 類,檢視該類的繼承關系

public abstract class HttpJspBase extends HttpServlet implements HttpJspPage {
           

可以看到該類繼承了 HttpServlet ;那麼 _01_002dhello_002djsp_jsp 這個類就間接的繼承了 HttpServlet ,也就說明 _01_002dhello_002djsp_jsp 是一個 servlet。

在HttpJspBase類中我們發現了Servlet類中的一個重要方法service(HttpServletRequest request, HttpServletResponse response)

public final void service(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        _jspService(request, response);
    }
    @Override
    public abstract void _jspService(HttpServletRequest request,
                                     HttpServletResponse response)
        throws ServletException, IOException;
           

發現service方法調用了一個抽象方法_jspService 思考:java基礎中抽象方法由誰實作?

抽象方法由其子類去實作
           

是以檢視 _01_002dhello_002djsp_jsp 類的代碼,可以看到有一個名為 _jspService() 的方法,該方法就是每次通路 jsp 時自動執行的方法。

而在 _jspService() 方法中可以看到往浏覽器寫标簽的代碼:

out.write("\r\n");
      out.write("\r\n");
      out.write("<html>\r\n");
      out.write("<head>\r\n");
      out.write("    <title>Hello JSP</title>\r\n");
      out.write("</head>\r\n");
      out.write("<body>\r\n");
      out.write("    <h1>你好 JSP</h1>\r\n");
      out.write("    ");

        System.out.println("你好 Java代碼");
    
      out.write("\r\n");
      out.write("</body>\r\n");
      out.write("</html>\r\n");
           

以前我們自己寫 servlet 時,現有由Tomcat自動生成這部分代碼,這也就是JSP的作用:簡化Servlet的開發

4,JSP 腳本

JSP腳本用于在 JSP頁面内定義 Java代碼

4.1 JSP 腳本分類

JSP 腳本有如下三個分類:

  • <%...%>:内容會直接放到_jspService()方法之中
  • <%=…%>:内容會放到out.print()中,作為out.print()的參數
  • <%!…%>:内容會放到_jspService()方法之外,被類直接包含 我們主要學習前兩種分類。 代碼示範:

建立02script.jsp,并書寫内容

web-6-深入了解JSP:探索其核心概念和特性
<%
        String name = "張三";
        //注意:最好泛型不要省略
        List<String> listStrs = new ArrayList<String>();
        listStrs.add("王五");
        listStrs.add("老六");
        listStrs.add("趙四");
    %>
           

通過浏覽器通路 02script.jsp 後,檢視轉換的 _02script_jsp.java 檔案,name和listStrs 變量定義在了 _jspService() 方法中

String name = "張三";
        //注意:最好泛型不要省略
        List<String> listStrs = new ArrayList<String>();
        listStrs.add("王五");
        listStrs.add("老六");
        listStrs.add("趙四");
           

在 02script.jsp 中書寫

<%=name%>
<%=listStrs%>
           

通過浏覽器通路 02script.jsp 後,檢視轉換的 _02script_jsp.java 檔案,該腳本的内容被放在了 out.print() 中,作為參數

out.print(name);
      out.write("\r\n");
      out.write("    ");
      out.print(listStrs);
           

浏覽器輸出結果:

web-6-深入了解JSP:探索其核心概念和特性

在 hello.jsp 中書寫

<%!
    String address="河南";
    void test(){
        System.out.println(address);
    }
%>
           

通過浏覽器通路 02script.jsp 後,檢視轉換的 _02script_jsp.java 檔案,該腳本的内容被放在了成員位置

web-6-深入了解JSP:探索其核心概念和特性

4.2 JSP 缺點

通過上面的案例,我們可以看到 JSP 的很多缺點。

由于 JSP頁面内,既可以定義 HTML 标簽,又可以定義 Java代碼,造成了以下問題:

  • 書寫麻煩:特别是複雜的頁面 既要寫 HTML 标簽,還要寫 Java 代碼
  • 閱讀麻煩
  • 複雜度高:運作需要依賴于各種環境,JRE,JSP容器,JavaEE…
  • 占記憶體和磁盤:JSP會自動生成.java和.class檔案占磁盤,運作的是.class檔案占記憶體
  • 調試困難:出錯後,需要找到自動生成的.java檔案進行調試
  • 不利于團隊協作:前端人員不會 Java,後端人員不精 HTML 如果頁面布局發生變化,前端工程師對靜态頁面進行修改,然後再交給後端工程師,由後端工程師再将該頁面改為 JSP 頁面 由于上述的問題, JSP 已逐漸退出曆史舞台

思考:那為什麼我還要介紹和學習JSP 呢?

  • 一些公司可能有些老項目還在用 JSP ,是以要求我們必須動 JSP
  • 我們如果不經曆這些複雜的過程,就不能展現後面階段開發的簡單

接下來我們來學習第三階段,使用 EL表達式 和 JSTL 标簽庫替換 JSP 中的 Java 代碼。

5.EL 表達式

5.1 概述

EL(全稱Expression Language )表達式語言,用于簡化 JSP 頁面内的 Java 代碼。

EL 表達式的主要作用是 擷取資料。其實就是從域對象中擷取資料,然後将資料展示在頁面上。

而 EL 表達式的文法也比較簡單,**${expression}** 。 例子:

${name}:擷取域中存儲的key為name的資料
可以了解為從map集合中擷取key為name對應的value值
           
web-6-深入了解JSP:探索其核心概念和特性

5.2 代碼示範

  • 定義servlet,在 servlet 中封裝一些資料并存儲到 request 域對象中并轉發到 03el.jsp 頁面。 建立ELServlet
package com.zbbmeta.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("/el")
public class ELServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //get請求
        // 1.準備資料
        String name = "張三";
        String address = "廣東省廣州市天河區";
        Integer age = 30;
        // 2.存儲資料到request域中
        request.setAttribute("name", name);
        request.setAttribute("address", address);
        request.setAttribute("age", age);
        // 3.轉發到jsp
         request.getRequestDispatcher("03el.jsp").forward(request, response);
        // request.getRequestDispatcher("/04if.jsp").forward(request, response);
        // request.getRequestDispatcher("/05foreach.jsp").forward(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //post請求
        doGet(request, response);
    }
}
           
注意:此處需要用轉發,因為轉發才可以使用 request 對象作為域對象進行資料共享
  • 在 03el.jsp 中通過 EL表達式 擷取資料
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>EL</title>
</head>
<body>
    <%--取出request存儲的資料--%>
    姓名: ${name} <br/>
    位址: ${address} <br/>
    年齡: ${age} <br/>
    不存在的: ${address1} <br/>
</body>
</html>
           
  • 在浏覽器的位址欄輸入 http://localhost:8080/jsp/el ,頁面效果如下:
web-6-深入了解JSP:探索其核心概念和特性

5.3 域對象

JavaWeb中有四大域對象,分别是:

  • page:目前頁面有效
  • request:目前請求有效
  • session:目前會話有效
  • application:目前應用有效

el 表達式擷取資料,會依次從這4個域中尋找,直到找到為止。而這四個域對象的作用範圍如下圖所示

web-6-深入了解JSP:探索其核心概念和特性

例如: ${name},el 表達式擷取資料,會先從page域對象中擷取資料,如果沒有再到 requet 域對象中擷取資料,如果再沒有再到 session 域對象中擷取,如果還沒有才會到 application 中擷取資料,,如果application中沒有擷取到資料,也不會報錯。

6.JSTL标簽

6.1 概述

JSP标準标簽庫(Jsp Standarded Tag Library) ,使用标簽取代JSP頁面上的Java代碼。如下代碼就是JSTL标簽

<c:if test="${flag == 1}">
    男
</c:if>
<c:if test="${flag == 2}">
    女
</c:if>
           

上面代碼看起來是不是比 JSP 中嵌套 Java 代碼看起來舒服好了。而且前端工程師對标簽是特别敏感的,他們看到這段代碼是能看懂的。

JSTL 提供了很多标簽,這裡隻對兩個最常用的标簽進行講解,<c:forEach> 标簽和 <c:if> 标簽。

JSTL 使用也是比較簡單的,分為如下步驟:

  • 導入坐标
<dependency>
    <groupId>jstl</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>
           
  • 在JSP頁面上引入JSTL标簽庫 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
  • 使用标簽

6.2 if 标簽

<c:if>:相當于 if 判斷

  • 屬性:test,用于定義條件表達式
<c:if test="true">
    true
</c:if>
<c:if test="false">
    false
</c:if>
           

代碼示範:

  • 定義一個 IFServlet ,在該 IFServlet 中向 request 域對象中添加共享資料
package com.zbbmeta.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("/ifservlet")
public class IFServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //get請求
        // 1.準備資料
        String name = "張三";
        String address = "廣東省廣州市天河區";
        Integer age = 30;
        // 2.存儲資料到request域中
        request.setAttribute("name", name);
        request.setAttribute("address", address);
        request.setAttribute("age", age);
        // 3.轉發到jsp
         request.getRequestDispatcher("04if.jsp").forward(request, response);
        // request.getRequestDispatcher("/05foreach.jsp").forward(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //post請求
        doGet(request, response);
    }
}
           
  • 定義 04if.jsp 頁面,在該頁面使用 <c:if> 标簽
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>JSTL的if标簽</title>
</head>
<body>
    <%-- c:if:來完成邏輯判斷,替代java中的if --%>
    <%--c:if 條件直接使用boolean類型--%>
    <c:if test="true">
        true
    </c:if>
    <c:if test="false">
        false
    </c:if>

    <%--c:if 條件從作用域中取出資料比較, 要通路 /demo01 這個Servlet, Servlet會儲存age到request中,這邊才能取到age--%>
    <c:if test="${age < 18}">
        未成年
    </c:if>
    <c:if test="${age >= 18}">
        成年
    </c:if>
</body>
</html>
           

注意: 在該頁面已經要引入 JSTL核心标簽庫

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

  • 在浏覽器中輸入http://localhost:8080/jsp/ifservlet,顯示結果
web-6-深入了解JSP:探索其核心概念和特性

6.3 forEach 标簽

<c:forEach>:相當于 for 循環。java中有增強for循環和普通for循環,JSTL 中的 <c:forEach> 也有兩種用法

6.3.1 用法一

類似于 Java 中的增強for循環。涉及到的 <c:forEach> 中的屬性如下

  • items:被周遊的容器
  • var:周遊産生的臨時變量
  • varStatus:周遊狀态對象

如下代碼,是從域對象中擷取名為 students 資料,該資料是一個集合;周遊周遊,并給該集合中的每一個元素起名為 student,是 Student對象。在循環裡面使用 EL表達式擷取每一個Brand對象的屬性值

<c:forEach items="${students}" var="student" varStatus="sta">
         <tr align="center">
             <td>${sta.count}</td>
             <td>${student.name}</td>
             <td>${student.address}</td>
             <td>${student.age}</td>
             <c:if test="${student.status==1}">
                 <td>啟用</td>
             </c:if>
             <c:if test="${student.status==0}">
                 <td>禁用</td>
             </c:if>
             <td><a href="#">修改</a> <a href="#">删除</a></td>
         </tr>
    </c:forEach>
           

代碼示範:

  • 定義一個實體類Student 。
package com.zbbmeta.entity;

public class Student {
    private String name;
    private String address;
    private Integer age;

    private Integer status;

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public String getName() {
        return name;
    }

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

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public Integer getAge() {
        return age;
    }

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

    public Student(String name, String address, Integer age, Integer status) {
        this.name = name;
        this.address = address;
        this.age = age;
        this.status = status;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                ", age=" + age +
                ", status=" + status +
                '}';
    }
}
           
  • 定義一個Servlet類命名為ForeachServlet 。
package com.zbbmeta.servlet;

import com.zbbmeta.entity.Student;
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("/forservlet")
public class ForeachServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //get請求
        // 1.準備資料
        List<Student> students = new ArrayList<>();
        students.add(new Student("王五","廣東省廣州市天河區",30,0));
        students.add(new Student("李四","廣東省江門市",40,1));
        students.add(new Student("老劉","廣東省珠海市香洲區",60,0));

        // 2.存儲資料到request域中
        request.setAttribute("students", students);
        // 3.轉發到jsp
         request.getRequestDispatcher("/05foreach.jsp").forward(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //post請求
        doGet(request, response);
    }
}

           
  • 定義名為 05foreach.jsp 頁面,内容如下:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>JSTL的foreach标簽</title>
</head>
<body>

<input type="button" value="新增"><br>
<hr>

<table border="1" cellspacing="0" width="70%">
    <tr>
        <th>序号</th>
        <th>姓名</th>
        <th>位址</th>
        <th>年齡</th>
        <th>操作</th>
    </tr>
    <%--c:forEach标簽: 周遊
        items屬性: 要周遊的資料
        var屬性: 變量名,儲存周遊到元素
        varStatus屬性: 周遊時的狀态
            index: 索引,從0開始
            count: 計數器,從1開始
        --%>
    <c:forEach items="${students}" var="student" varStatus="sta">
         <tr align="center">
             <td>${sta.count}</td>
             <td>${student.name}</td>
             <td>${student.address}</td>
             <td>${student.age}</td>
             <c:if test="${student.status==1}">
                 <td>啟用</td>
             </c:if>
             <c:if test="${student.status==0}">
                 <td>禁用</td>
             </c:if>
             <td><a href="#">修改</a> <a href="#">删除</a></td>
         </tr>
    </c:forEach>
</table>


</body>
</html>
           
  • 通路浏覽器位址:http://localhost:8080/jsp/forservlet顯示結果:
web-6-深入了解JSP:探索其核心概念和特性

6.3.2 用法二

類似于 Java 中的普通for循環。涉及到的 <c:forEach> 中的屬性如下

  • begin:開始數
  • end:結束數
  • step:步長

執行個體代碼:

從0循環到10,變量名是 i ,每次自增1

<hr/>

<%--類似于普通for循環--%>
<c:forEach var="i" begin="1" end="10" step="2">
    ${i}
</c:forEach>
           

05foreach.jsp完整代碼

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>JSTL的foreach标簽</title>
</head>
<body>

<input type="button" value="新增"><br>
<hr>

<table border="1" cellspacing="0" width="70%">
    <tr>
        <th>序号</th>
        <th>姓名</th>
        <th>位址</th>
        <th>年齡</th>
        <th>操作</th>
    </tr>
    <%--c:forEach标簽: 周遊
        items屬性: 要周遊的資料
        var屬性: 變量名,儲存周遊到元素
        varStatus屬性: 周遊時的狀态
            index: 索引,從0開始
            count: 計數器,從1開始
        --%>
    <c:forEach items="${students}" var="student" varStatus="sta">
         <tr align="center">
             <td>${sta.count}</td>
             <td>${student.name}</td>
             <td>${student.address}</td>
             <td>${student.age}</td>
             <c:if test="${student.status==1}">
                 <td>啟用</td>
             </c:if>
             <c:if test="${student.status==0}">
                 <td>禁用</td>
             </c:if>
             <td><a href="#">修改</a> <a href="#">删除</a></td>
         </tr>
    </c:forEach>
</table>

<hr/>

<%--類似于普通for循環--%>
<c:forEach var="i" begin="1" end="10" step="2">
    ${i}
</c:forEach>
</body>
</html>
           

繼續閱讀