Web-6-深入理解JSP:探索其核心概念和特性
JSP
今日目標:
理解 JSP 及 JSP 原理
能在 JSP中使用 EL表達式 和 JSTL標簽
1、JSP 概述
JSP(全稱:Java Server Pages):Java 服務端頁面。是一種動態(tài)的網頁技術,其中既可以定義 HTML、JS、CSS等靜態(tài)內容,還可以定義 Java代碼的動態(tài)內容,也就是 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 顯示結果為:
思考:JSP 能做什么呢?
JSP 作用:簡化開發(fā),避免了在Servlet中直接輸出HTML標簽。
2、JSP 快速入門
接下來我們做一個簡單的快速入門代碼。
(1)搭建環(huán)境
創(chuàng)建一個maven的 web 項目,項目結構如下:
pom.xml 文件內容如下:
<?xml versinotallow="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)導入 JSP 依賴
在 dependencies 標簽中導入 JSP 的依賴,如下:
<!-- jsp-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
<!--因為tomcat存在jsp的jar包,我們只是在開發(fā)階段使用,索引scope選擇為provided-->
<scope>provided</scope>
</dependency>
(3)創(chuàng)建 jsp 頁面
在項目的 webapp 下創(chuàng)建jsp頁面。
通過上面方式創(chuàng)建一個名為 hello.jsp 的頁面。
(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>
(5)測試
啟動服務器并在瀏覽器地址欄輸入 http://localhost:8080/jsp/01-hello-jsp.jsp,我們可以在頁面上看到如下內容。
在 idea 的控制臺看到輸出的 你好 Java代碼 內容。
3、JSP 原理
思考:為什么還可以寫 Java 代碼呢?
因為JSP 本質上就是一個 Servlet。
- 瀏覽器第一次訪問 01-hello-jsp.jsp 頁面。
- tomcat 會將 01-hello-jsp.jsp 轉換為名為 _01_002dhello_002djsp_jsp.java 的一個 Servlet。
- tomcat 再將轉換的 servlet 編譯成字節(jié)碼文件 _01_002dhello_002djsp_jsp.class。
- tomcat 會執(zhí)行該字節(jié)碼文件,向外提供服務。
打開 _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類中我們發(fā)現了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;
發(fā)現service方法調用了一個抽象方法_jspService 思考:java基礎中抽象方法由誰實現?
抽象方法由其子類去實現
所以查看 _01_002dhello_002djsp_jsp 類的代碼,可以看到有一個名為 _jspService() 的方法,該方法就是每次訪問 jsp 時自動執(zhí)行的方法。
而在 _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的開發(fā)。
4、JSP 腳本
JSP腳本用于在 JSP頁面內定義 Java代碼.
(1)JSP 腳本分類
JSP 腳本有如下三個分類:
- <%...%>:內容會直接放到_jspService()方法之中。
- <%=…%>:內容會放到out.print()中,作為out.print()的參數。
- <%!…%>:內容會放到_jspService()方法之外,被類直接包含 我們主要學習前兩種分類。 代碼演示:
創(chuàng)建02script.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);
瀏覽器輸出結果:
在 hello.jsp 中書寫。
<%!
String address="河南";
void test(){
System.out.println(address);
}
%>
通過瀏覽器訪問 02script.jsp 后,查看轉換的 _02script_jsp.java 文件,該腳本的內容被放在了成員位置。
(2)JSP 缺點
通過上面的案例,我們可以看到 JSP 的很多缺點。
由于 JSP頁面內,既可以定義 HTML 標簽,又可以定義 Java代碼,造成了以下問題:
- 書寫麻煩:特別是復雜的頁面 既要寫 HTML 標簽,還要寫 Java 代碼.
- 閱讀麻煩。
- 復雜度高:運行需要依賴于各種環(huán)境,JRE,JSP容器,JavaEE…
- 占內存和磁盤:JSP會自動生成.java和.class文件占磁盤,運行的是.class文件占內存。
- 調試困難:出錯后,需要找到自動生成的.java文件進行調試。
- 不利于團隊協(xié)作:前端人員不會 Java,后端人員不精 HTML 如果頁面布局發(fā)生變化,前端工程師對靜態(tài)頁面進行修改,然后再交給后端工程師,由后端工程師再將該頁面改為 JSP 頁面 由于上述的問題, JSP 已逐漸退出歷史舞臺。
思考:那為什么我還要介紹和學習JSP 呢?
- 一些公司可能有些老項目還在用 JSP ,所以要求我們必須動 JSP。
- 我們如果不經歷這些復雜的過程,就不能體現后面階段開發(fā)的簡單。
接下來我們來學習第三階段,使用 EL表達式 和 JSTL 標簽庫替換 JSP 中的 Java 代碼。
5、EL 表達式
(1)概述
EL(全稱Expression Language )表達式語言,用于簡化 JSP 頁面內的 Java 代碼。
EL 表達式的主要作用是 獲取數據。其實就是從域對象中獲取數據,然后將數據展示在頁面上。
而 EL 表達式的語法也比較簡單,**${expression}** 。 例子:
${name}:獲取域中存儲的key為name的數據
可以理解為從map集合中獲取key為name對應的value值
(2)代碼演示
- 定義servlet,在 servlet 中封裝一些數據并存儲到 request 域對象中并轉發(fā)到 03el.jsp 頁面。 創(chuàng)建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 = "廣東省廣州市天河區(qū)";
Integer age = 30;
// 2.存儲數據到request域中
request.setAttribute("name", name);
request.setAttribute("address", address);
request.setAttribute("age", age);
// 3.轉發(fā)到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);
}
}
注意:此處需要用轉發(fā),因為轉發(fā)才可以使用 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 ,頁面效果如下:
(3)域對象
JavaWeb中有四大域對象,分別是:
- page:當前頁面有效。
- request:當前請求有效。
- session:當前會話有效。
- application:當前應用有效。
el 表達式獲取數據,會依次從這4個域中尋找,直到找到為止。而這四個域對象的作用范圍如下圖所示:
例如: ${name},el 表達式獲取數據,會先從page域對象中獲取數據,如果沒有再到 requet 域對象中獲取數據,如果再沒有再到 session 域對象中獲取,如果還沒有才會到 application 中獲取數據,,如果application中沒有獲取到數據,也不會報錯。
6、JSTL標簽
(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" %>。
- 使用標簽
(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 = "廣東省廣州市天河區(qū)";
Integer age = 30;
// 2.存儲數據到request域中
request.setAttribute("name", name);
request.setAttribute("address", address);
request.setAttribute("age", age);
// 3.轉發(fā)到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,顯示結果。
(3)forEach 標簽
<c:forEach>:相當于 for 循環(huán)。java中有增強for循環(huán)和普通for循環(huán),JSTL 中的 <c:forEach> 也有兩種用法
用法一
類似于 Java 中的增強for循環(huán)。涉及到的 <c:forEach> 中的屬性如下:
- items:被遍歷的容器。
- var:遍歷產生的臨時變量。
- varStatus:遍歷狀態(tài)對象。
如下代碼,是從域對象中獲取名為 students 數據,該數據是一個集合;遍歷遍歷,并給該集合中的每一個元素起名為 student,是 Student對象。在循環(huán)里面使用 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("王五","廣東省廣州市天河區(qū)",30,0));
students.add(new Student("李四","廣東省江門市",40,1));
students.add(new Student("老劉","廣東省珠海市香洲區(qū)",60,0));
// 2.存儲數據到request域中
request.setAttribute("students", students);
// 3.轉發(fā)到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屬性: 遍歷時的狀態(tài)
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顯示結果:
用法二
類似于 Java 中的普通for循環(huán)。涉及到的 <c:forEach> 中的屬性如下
- begin:開始數
- end:結束數
- step:步長
實例代碼:
從0循環(huán)到10,變量名是 i ,每次自增1。
<hr/>
<%--類似于普通for循環(huán)--%>
<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屬性: 遍歷時的狀態(tài)
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循環(huán)--%>
<c:forEach var="i" begin="1" end="10" step="2">
${i}
</c:forEach>
</body>
</html>