為什么要學習 JSP
Servlet 的短板:
Servlet 的出現,是為了解決動態輸出網頁的問題。
雖然這樣做目的能達到,但是存在一些缺陷:
- 在 Servlet 輸出網頁片段非常惡心 (可讀性差,維護起來也很麻煩)
- 沒有體現 責任分離 的原則(做自己擅長做的事)
責任分離
- Servlet 能夠很好地組織業務邏輯代碼,但是在 Java 源文件中通過字符串拼接的方式生成動態 HTML 內容會導致代碼維護困難、可讀性差
- JSP 雖然規避了 Servlet 在生成 HTML 內容方面的劣勢,但是在 HTML 中混入大量、復雜的業務邏輯同樣也是不可取的
- 參考:知乎@David
- 注意:JSP實質上就是一個Servlet
MVC 模式
既然 Servlet 和 JSP 都有各自的優勢和短板,那么為什么不結合起來揚長避短呢?答案是肯定的——MVC(Model-View-Controller)模式非常適合解決這一問題。
MVC模式(Model-View-Controller)是軟件工程中的一種軟件架構模式,把軟件系統分為三個基本部分:模型(Model)、視圖(View)和控制器(Controller):
- Controller——負責轉發請求,對請求進行處理
- View——負責界面顯示
- Model——業務功能編寫(例如算法實現)、數據庫設計以及數據存取操作實現
在JSP/Servlet開發的軟件系統中,這三個部分的描述如下所示:
1.Web瀏覽器發送HTTP請求到服務端,被Controller(Servlet)獲取並進行處理(例如參數解析、請求轉發)
2.Controller(Servlet)調用核心業務邏輯——Model部分,獲得結果
3.Controller(Servlet)將邏輯處理結果交給View(JSP),動態輸出HTML內容
4.動態生成的HTML內容返回到瀏覽器顯示
MVC模式在Web開發中的好處是非常明顯,它規避了JSP與Servlet各自的短板,Servlet只負責業務邏輯而不會通過out.append()動態生成HTML代碼;JSP中也不會充斥着大量的業務代碼。這大大提高了代碼的可讀性和可維護性。
- 參考:知乎@David
JSP 的執行原理
- 總結:
當訪問一個 JSP 頁面時,該頁面請求將會講給服務器中的 JSP 引擎去處理,它負責解釋和執行 JSP 頁面,每個 JSP 頁面在第一次被訪問時,JSP 引擎就會將它翻譯成一個繼承自org.apache.jasper.runtime.HttpJspBase
類的 Servlet 源程序,接着再編譯成 class 類文件,再由 Web 容器像調用普通 Servlet 程序一樣的方式來裝載和解釋執行這個由 JSP 頁面翻譯成的 Servlet 程序。
JSP 的語法
像這樣冗雜繁復的知識點,直接給兩個好一點的鏈接記下就好了:
1.W3Cschool
2.菜鳥教程
JSP 三大指令
- 特點:
並不向客戶端產生任何輸出,指令在 JSP 整個文件范圍內有效,並且為翻譯階段提供了全局信息 - 指令的語法格式:
<%@ 指令名稱 屬性名=屬性值 屬性名=屬性值%>
——【page指令】——
- 作用:
定義 JSP 頁面的各種屬性 - 屬性:
1.language:指示JSP頁面中使用腳本語言。默認值java,目前只支持java。
2.extends:指示 JSP 對應的 Servlet 類的父類。不要修改。
3.*
import:導入JSP中的Java腳本使用到的類或包。(如同Java中的import語句)
JSP 引擎自動導入以下包中的類:
javax.servlet.*
javax.servlet.http.*
javax.servlet.jsp.*
注意:一個import屬性可以導入多個包,用逗號分隔。
4.*
sessioin:指示JSP頁面是否創建 HttpSession 對象。默認值是true,創建
5.*
buffer:指示 JSP 用的輸出流的緩存大小.默認值是8Kb。
6.autoFlush:自動刷新輸出流的緩存。
7.isThreadSafe:指示頁面是否是線程安全的(過時的)。默認是true。
true:不安全的。
false:安全的。指示 JSP 對應的 Servlet 實現 SingleThreadModel 接口。
8.*
errorPage:指示當前頁面出錯后轉向(轉發)的頁面。
> 配置全局錯誤提示頁面:
> web.xml 文件中添加:
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/error.jsp</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/404.jsp</location>
</error-page>
9.*
isErrorPage:指示當前頁面是否產生 Exception 對象。
10.*
contentType:指定當前頁面的 MIME 類型。作用與 Servlet 中的response.setContentType()
作用完全一致
11.*
pageEncoding:通知引擎讀取 JSP 時采用的編碼(因為要翻譯)
12.*
isELIgnored:是否忽略EL表達式。${1+1}。默認值是false。
- page 指令最簡單的使用方式:
<%@ page pageEncoding="UTF-8"%>
——【include】——
- 作用:
包含其他的組件 - 語法:
<%@include file=""%>
file 指定要包含的目標組件。路徑如果以 "/"(當前應用)就是絕對路徑。 - 原理:
把目標組件的內容加到源組件中,輸出結果。
靜態包含和動態包含的區別:
-
靜態包含:
<%@include file="被包含的頁面的路徑"%>
包含的時機:在 JSP 文件被翻譯的時候合並在一起
最終會被翻譯成一個 class 文件 -
動態包含:
<jsp:include page="被包含頁面的路徑"></jsp:include>
包含的時機:在運行階段合並代碼
最終將得到兩個 class 文件 -
總結:在實際開發中,能用靜的就別用動的
——【taglib】——
- 作用:
引入外部的標簽 - 語法:
<%@taglib uri="標簽名稱空間" prefix="前綴"%>
例如:<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
JSP 九大內置對象
內置對象:JSP 中事先創建好的對象,可以直接拿來使用
名稱 | 類型 | 描述 |
---|---|---|
pageContext | PageContext | 表示當前的JSP對象 |
request | HttpServletRequest | 表示一次請求對象 |
session | HttpSession | 表示一次會話對象,session="true" |
application | ServletContext | 表示當前應用對象 |
response | HttpServletResponse | 表示一次響應對象 |
exception | Throwable | 表示異常對象,isErrorPage="true" |
config | ServletConfig | 表示當前JSP的配置對象 |
out | JspWriter | 表示一個輸出流對象 |
page | Object | 表示當前頁面 |
JSP 四大作用域
名稱 | 類型 | 描述 |
---|---|---|
pageContext | PageContext | 表示當前的JSP對象 |
request | HttpServletRequest | 表示一次請求對象 |
session | HttpSession | 表示一次會話對象,session="true" |
application | ServletContext | 表示當前應用對象 |
EL(表達式語言)
- 需求:從作用域中獲取共享數據,如果沒有對應的數據,返回空字符串
在PageContext中提供了下面的方法:abstract Object findAttribute(String name)
來獲取共享數據,從page,request,session,application作用域中按順序搜索,如果找到立即返回,反之,返回null
所以我們可以這樣來完成要求:
<%=pageContext.findAttribute("msg")==null?"":pageContext.findAttribute("msg") %>
這樣的代碼雖然能夠完成需求,但是總的來說:太麻煩!
-
如果我們使用 EL 表達式,該如何實現呢?
${msg}
等價於<%=pageContext.findAttribute("msg")==null?"":pageContext.findAttribute("msg") %>
這樣看起來就簡單多了! -
EL的特點:
1.從作用域中獲取共享數據
2.從page,request,session,application作用域中按順序搜索
3.如果共享數據為null,就輸出空字符串(這是EL最重要的特點)
使用EL表達式從指定的作用域中獲取共享數據:
-
使用EL表達式訪問對象的屬性的兩種方式:
1.${對象.屬性名} :通常使用這種方式,屬性名比較規范
2.${對象[“屬性名”]} :當屬性名不規范的時候使用這種方式,比如:name-age -
使用El表達式獲取應用的上下文路徑:
在EL表達式中有一個隱含的對象pageContext
而在pageContext中有一個request屬性,在request對象中有一個contextPath屬性,那么獲取contextPage的方法:
${pageContext.request.contextPath}
在 Tomcat 7 以后,EL表達式不僅支持屬性的訪問,而且還支持訪問方法
${pageContext.getRequest().getContextPath()}
-
empty 運算符:
empty 運算符主要用來判斷值是否為空(NULL,空字符串,空集合),返回 true / false
JSTL
- 作用:消除 JSP 中的 Java 代碼
- 在 JSP 中使用 JSTL 的步驟:
1.引入入jar包:在 Tomcat 中的實例項目 examples 中找到對應的兩個jar包
standard-1.1.2.jar ,jstl-1.1.2.jar
2.在對應的 JSP 頁面中引入要使用的標簽庫,比如引入核心標簽庫
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
下面列出 JSTL 主要提供的 5 大類標簽庫(搬自菜鳥教程),先對這些標簽初步有一個印象,然后下面給一些常用的標簽的一些用法。
JSTL 核心標簽
核心標簽是最常用的JSTL標簽。引用核心標簽庫的語法如下:
<%@ taglib prefix="c"
uri="http://java.sun.com/jsp/jstl/core" %>
標簽 | 描述 |
---|---|
<c:out> |
用於在JSP中顯示數據,就像<%= ... > |
<c:set> |
用於保存數據 |
<c:remove> |
用於刪除數據 |
<c:catch> |
用來處理產生錯誤的異常狀況,並且將錯誤信息儲存起來 |
<c:if> |
與我們在一般程序中用的if一樣 |
<c:choose> |
本身只當做<c:when>和<c:otherwise>的父標簽 |
<c:when> |
<c:choose>的子標簽,用來判斷條件是否成立 |
<c:otherwise> |
<c:choose>的子標簽,接在<c:when>標簽后,當<c:when>標簽判斷為false時被執行 |
<c:import> |
檢索一個絕對或相對 URL,然后將其內容暴露給頁面 |
<c:forEach> |
基礎迭代標簽,接受多種集合類型 |
<c:forTokens> |
根據指定的分隔符來分隔內容並迭代輸出 |
<c:param> |
用來給包含或重定向的頁面傳遞參數 |
<c:redirect> |
重定向至一個新的URL. |
<c:url> |
使用可選的查詢參數來創造一個URL |
格式化標簽
JSTL格式化標簽用來格式化並輸出文本、日期、時間、數字。引用格式化標簽庫的語法如下:
<%@ taglib prefix="fmt"
uri="http://java.sun.com/jsp/jstl/fmt" %>
標簽 | 描述 |
---|---|
<fmt:formatNumber> |
使用指定的格式或精度格式化數字 |
<fmt:parseNumber> |
解析一個代表着數字,貨幣或百分比的字符串 |
<fmt:formatDate> |
使用指定的風格或模式格式化日期和時間 |
<fmt:parseDate> |
解析一個代表着日期或時間的字符串 |
<fmt:bundle> |
綁定資源 |
<fmt:setLocale> |
指定地區 |
<fmt:setBundle> |
綁定資源 |
<fmt:timeZone> |
指定時區 |
<fmt:setTimeZone> |
指定時區 |
<fmt:message> |
顯示資源配置文件信息 |
<fmt:requestEncoding> |
設置request的字符編碼 |
SQL標簽
JSTL SQL標簽庫提供了與關系型數據庫(Oracle,MySQL,SQL Server等等)進行交互的標簽。引用SQL標簽庫的語法如下:
<%@ taglib prefix="sql"
uri="http://java.sun.com/jsp/jstl/sql" %>
標簽 | 描述 |
---|---|
<sql:setDataSource> |
指定數據源 |
<sql:query> |
運行SQL查詢語句 |
<sql:update> |
運行SQL更新語句 |
<sql:param> |
將SQL語句中的參數設為指定值 |
<sql:dateParam> |
將SQL語句中的日期參數設為指定的java.util.Date 對象值 |
<sql:transaction> |
在共享數據庫連接中提供嵌套的數據庫行為元素,將所有語句以一個事務的形式來運行 |
XML 標簽
JSTL XML標簽庫提供了創建和操作XML文檔的標簽。引用XML標簽庫的語法如下:
<%@ taglib prefix="x"
uri="http://java.sun.com/jsp/jstl/xml" %>
在使用xml標簽前,你必須將XML 和 XPath 的相關包拷貝至你的<Tomcat 安裝目錄>\lib下:
-
XercesImpl.jar
-
xalan.jar
標簽 | 描述 |
---|---|
<x:out> |
與<%= ... >,類似,不過只用於XPath表達式 |
<x:parse> |
解析 XML 數據 |
<x:set> |
設置XPath表達式 |
<x:if> |
判斷XPath表達式,若為真,則執行本體中的內容,否則跳過本體 |
<x:forEach> |
迭代XML文檔中的節點 |
<x:choose> |
<x:when>和<x:otherwise>的父標簽 |
<x:when> |
<x:choose>的子標簽,用來進行條件判斷 |
<x:otherwise> |
<x:choose>的子標簽,當<x:when>判斷為false時被執行 |
<x:transform> |
將XSL轉換應用在XML文檔中 |
<x:param> |
與<x:transform>共同使用,用於設置XSL樣式表 |
JSTL函數
JSTL包含一系列標准函數,大部分是通用的字符串處理函數。引用JSTL函數庫的語法如下:
<%@ taglib prefix="fn"
uri="http://java.sun.com/jsp/jstl/functions" %>
函數 | 描述 |
---|---|
fn:contains() | 測試輸入的字符串是否包含指定的子串 |
fn:containsIgnoreCase() | 測試輸入的字符串是否包含指定的子串,大小寫不敏感 |
fn:endsWith() | 測試輸入的字符串是否以指定的后綴結尾 |
fn:escapeXml() | 跳過可以作為XML標記的字符 |
fn:indexOf() | 返回指定字符串在輸入字符串中出現的位置 |
fn:join() | 將數組中的元素合成一個字符串然后輸出 |
fn:length() | 返回字符串長度 |
fn:replace() | 將輸入字符串中指定的位置替換為指定的字符串然后返回 |
fn:split() | 將字符串用指定的分隔符分隔然后組成一個子字符串數組並返回 |
fn:startsWith() | 測試輸入字符串是否以指定的前綴開始 |
fn:substring() | 返回字符串的子集 |
fn:substringAfter() | 返回字符串在指定子串之后的子集 |
fn:substringBefore() | 返回字符串在指定子串之前的子集 |
fn:toLowerCase() | 將字符串中的字符轉為小寫 |
fn:toUpperCase() | 將字符串中的字符轉為大寫 |
fn:trim() | 移除首位的空白符 |
JSTL 中常用的標簽
1.邏輯判斷標簽(if,choose-when-otherwise)
- <c:if> 標簽
- 語法格式
<c:if test="<boolean>" var="<string>" scope="<string>">
...
</c:if>
- 屬性
<c:if>標簽有如下屬性:
屬性 | 描述 | 是否必要 | 默認值 |
---|---|---|---|
test | 條件 | 是 | 無 |
var | 用於存儲條件結果的變量 | 否 | 無 |
scope | var屬性的作用域 | 否 | page |
- 演示實例:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>c:if 標簽實例</title>
</head>
<body>
<c:set var="salary" scope="session" value="${2000*2}"/>
<c:if test="${salary > 2000}">
<p>我的工資為: <c:out value="${salary}"/><p>
</c:if>
</body>
</html>
運行結果如下:
我的工資為: 4000
<c:choose>, <c:when>, <c:otherwise>
標簽
<c:choose>
標簽與 Java switch 語句的功能一樣,用於在眾多選項中做出選擇。
switch 語句中有 case ,而<c:choose>
標簽中對應有<c:when>
,switch語句中有 default,而<c:choose>
標簽中有<c:otherwise>
。- 語法格式
<c:choose>
<c:when test="<boolean>">
...
</c:when>
<c:when test="<boolean>">
...
</c:when>
...
...
<c:otherwise>
...
</c:otherwise>
</c:choose>
- 屬性
<c:choose>
標簽沒有屬性。
<c:when>
標簽只有一個屬性,在下表中有給出。
<c:otherwise>
標簽沒有屬性。
<c:when>
標簽的屬性如下:
屬性 | 描述 | 是否必要 | 默認值 |
---|---|---|---|
test | 條件 | 是 | 無 |
- 實例演示
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>c:choose 標簽實例</title>
</head>
<body>
<c:set var="salary" scope="session" value="${2000*2}"/>
<p>你的工資為 : <c:out value="${salary}"/></p>
<c:choose>
<c:when test="${salary <= 0}">
太慘了。
</c:when>
<c:when test="${salary > 1000}">
不錯的薪水,還能生活。
</c:when>
<c:otherwise>
什么都沒有。
</c:otherwise>
</c:choose>
</body>
</html>
運行結果如下:
你的工資為 : 4000
不錯的薪水,還能生活。
2.循環遍歷標簽(foreach)
- <c:forEach>標簽
- 語法格式
<c:forEach
items="<object>"
begin="<int>"
end="<int>"
step="<int>"
var="<string>"
varStatus="<string>">
...
- 屬性
<c:forEach>
標簽有如下屬性:
屬性 | 描述 | 是否必要 | 默認值 |
---|---|---|---|
items | 要被循環的信息 | 否 | 無 |
begin | 開始的元素(0=第一個元素,1=第二個元素) | 否 | 0 |
end | 最后一個元素(0=第一個元素,1=第二個元素) | 否 | Last element |
step | 每一次迭代的步長 | 否 | 1 |
var | 代表當前條目的變量名稱 | 否 | 無 |
varStatus | 代表循環狀態的變量名稱 | 否 | 無 |
- 實例演示
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
<title>c:forEach 標簽實例</title>
</head>
<body>
<c:forEach var="i" begin="1" end="5">
Item <c:out value="${i}"/><p>
</c:forEach>
</body>
</html>
運行結果如下:
Item 1
Item 2
Item 3
Item 4
Item 5
3.在 JSP 中實現日期的格式化:
<fmt:formatDate>
標簽- 語法格式
<fmt:formatDate
value="<string>"
type="<string>"
dateStyle="<string>"
timeStyle="<string>"
pattern="<string>"
timeZone="<string>"
var="<string>"
scope="<string>"/>
- 屬性
<fmt:formatDate>
標簽有如下屬性:
屬性 | 描述 | 是否必要 | 默認值 |
---|---|---|---|
value | 要顯示的日期 | 是 | 無 |
type | DATE, TIME, 或 BOTH | 否 | date |
dateStyle | FULL, LONG, MEDIUM, SHORT, 或 DEFAULT | 否 | default |
timeStyle | FULL, LONG, MEDIUM, SHORT, 或 DEFAULT | 否 | default |
pattern | 自定義格式模式 | 否 | 無 |
timeZone | 顯示日期的時區 | 否 | 默認時區 |
var | 存儲格式化日期的變量名 | 否 | 顯示在頁面 |
scope | 存儲格式化日志變量的范圍 | 否 | 頁面 |
- 實例演示
<%@ 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="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<html>
<head>
<title>JSTL fmt:dateNumber 標簽</title>
</head>
<body>
<h3>日期格式化:</h3>
<c:set var="now" value="<%=new java.util.Date()%>" />
<p>日期格式化 (1): <fmt:formatDate type="time"
value="${now}" /></p>
<p>日期格式化 (2): <fmt:formatDate type="date"
value="${now}" /></p>
<p>日期格式化 (3): <fmt:formatDate type="both"
value="${now}" /></p>
<p>日期格式化 (4): <fmt:formatDate type="both"
dateStyle="short" timeStyle="short"
value="${now}" /></p>
<p>日期格式化 (5): <fmt:formatDate type="both"
dateStyle="medium" timeStyle="medium"
value="${now}" /></p>
<p>日期格式化 (6): <fmt:formatDate type="both"
dateStyle="long" timeStyle="long"
value="${now}" /></p>
<p>日期格式化 (7): <fmt:formatDate pattern="yyyy-MM-dd"
value="${now}" /></p>
</body>
</html>
以上實例運行結果:
日期格式化:
日期格式化 (1): 11:19:43
日期格式化 (2): 2016-6-26
日期格式化 (3): 2016-6-26 11:19:43
日期格式化 (4): 16-6-26 上午11:19
日期格式化 (5): 2016-6-26 11:19:43
日期格式化 (6): 2016年6月26日 上午11時19分43秒
日期格式化 (7): 2016-06-26
歡迎轉載,轉載請注明出處!
簡書ID:@我沒有三顆心臟
github:wmyskxz
歡迎關注公眾微信號:wmyskxz_javaweb
分享自己的Java Web學習之路以及各種Java學習資料