JSP概述
掌握了servlet后,就可以利用servlet來開發動態頁面了,但是使用Servlet開發動態頁面,存在種種問題,來看下面的例子:
使用Servlet來開發百度首頁:
分析上面的案例不難發現以下問題:
Html與Java代碼混合在一起,維護困難 |
---|
每一行都是一個println語句,效率低下 |
編譯器無法檢測問題,調試麻煩 |
JSP概念
全稱JavaServerPage服務器頁面,為了使開發動態頁面更加簡單而出現,本質上也是一個Servlet
動態與靜態
一個頁面如果不會隨着任何條件(時間,用戶信息...)改變而發生變化,那它就是靜態的,靜態頁面通常只能提供最基本的信息展示
動態頁面是會隨着訪問時的時間,地點,提交的數據不同而展示不同的內容,它就是動態頁面,例如可以根據登錄用戶的不同而展示的不同的購物信息,通常頁面的數據來自於數據庫
特點:
-
JSP可將原本都在Servlet中的java代碼與HTML分離,降低耦合,可維護性好
-
可編寫原生HTML,且編譯器會進行語法檢查,開發效率更高
-
JSP本質就是Servlet,被執行前會被先轉譯為java文件
-
注意:JSP文件需放在web(webContent)資源目錄下,后綴為.jsp
jsp執行過程
示例:
轉譯后的java文件解析
找到轉譯后的java文件
打開該文件
可以看到其原理與servlet完全相同,利用printWriter來向前台返回響應數據
JSP語法
表達式
語法格式:<%=表達式%>
作用:通過表達式可以輸出表達式的結果值。其本質就是執行了print語句;
示例:
注意:表達式中不能有分號,只能是一行
代碼塊
語法格式:<%代碼內容%>
作用:編寫任何Java代碼,通常是用來輸出產生HTML文本內容的,這是與普通HTML最大的區別,有了代碼塊你可以很輕松的實現動態頁面
示例:
聲明塊
語法格式:<%!代碼內容%>
作用:在JSP中聲明方法和變量
示例:
注釋
語法:
<%-- jsp注釋 --%> 頁面不可見
<!-- html注釋 --> 頁面可見
// /**/ 代碼塊內java注釋 轉譯jsp時會放到java文件中
指令
編譯指令
編譯指令,用於處理當前jsp全局配置,例如導入Java類,使用標簽庫,或內容編碼等,由Servlet引擎處理,在JSP轉譯Servlet時生效
通常位於JSP文件開始的地方,這樣可以保證要使用的資源已經被導入
指令名 | 作用 |
---|---|
include | 靜態引入其他JSP頁面 |
taglib | 導入標簽庫,設置標簽前綴等 |
page | 導入Java類,設置響應編碼等 |
語法:<%@ page 屬性名稱="屬性值">
示例:<%@ page import="java.util.HashMap" %>
動作指令
動作指令通常可以被JSP腳本替代,是對JSP腳本的HTML標准化寫法
指令名 | 作用 |
---|---|
jsp:forward | 執行頁面轉向,將請求的處理轉發到下一個頁面/servlet。 |
jsp:param | 用於傳遞參數,必須與其他支持參數的標簽一起使用。 |
jsp:include | 用於動態引入一個JSP頁面。 |
jsp:plugin | 用於下載JavaBean或Applet到客戶端執行。 |
jsp:useBean | 創建一個JavaBean實例。 |
jsp:setProperty | 設置JavaBean實例的屬性值。 |
jsp:getProperty | 輸出JavaBean實例的屬性值。 |
語法:<jsp:動作名稱 名稱="值">
示例<jsp:forward page="/FirstServlet"></jsp:forward>
JSP語法匯總
Cookie
由於HTTP協議狀態無連接的特性,造成了用戶狀態無法維持的問題,客戶端前腳剛剛登陸成功,后腳就要重新登陸,毫無體驗,為了解決這個問題,出現了Cookie,以及后來的session
- Cookie是服務器保存在瀏覽器的一小段文本數據(4k內),通常與用戶個人信息有關
- cookie數據以鍵值對形式存在
- 用於在HTTP協議下維持客戶端與服務器的狀態
- cookie具有時效性,在有效期內
- 每次請求相同的服務器(域名相同,路徑相同)時,瀏覽器都會自動將cookie放入請求頭一起發送給服務器
圖解:
Cookie相關方法
方法名稱 | 作用 |
---|---|
Cookie(key,value) | cookie的構造函數。 |
setMaxAge() | 設置cookie的最大存活時間。 |
getValue(key) | 獲取對應key的cookie值。 |
setValue(key,newValue) | 修改對應key的cookie的值。 |
response.addCookie(cookie) | 往響應中添加對應key的cookie對象。 |
利用Cookie來實現7天免登陸
用於處理登錄請求的LoginServlet:
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "LoginServlet",urlPatterns = "/LoginServlet")
public class LoginServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//登錄判斷邏輯 (實際開發中從請求中獲取參數匹配數據庫)
System.out.println("login success");
//創建Cookie對象
Cookie c = new Cookie("user","admin");
//設置cookie有效期
c.setMaxAge(60*60*24*7);
//添加cookie到響應中
response.addCookie(c);
}
}
用於作為用戶主頁的IndexServlet
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(name = "IndexServlet",urlPatterns = "/IndexServlet")
public class IndexServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//獲取請求中所有cookie
Cookie[] cs = request.getCookies();
//判斷是否有cookie
if(cs!=null){
String name = null;
//遍歷所有cookie
for (Cookie c: cs) {
//找到用戶名稱
if(c.getName().equals("user")){
name =c.getValue();
}
}
//在頁面輸出已經登錄過的用戶名稱
response.setContentType("text/html;charset=utf-8");
if(name != null){
response.getWriter().println("user:" + name);
}
}
}
}
在瀏覽器中先訪問LoginServlet,重新啟動瀏覽器,訪問IndexServlet可以看到之前登錄的用戶信息
注意:
如果沒有設置最大有效期,cookie則是臨時的瀏覽器進程關閉就消失了
Cookie解決了客戶端與服務器需要維持狀態的問題,但也帶來了新的問題
- cookie是存儲在客戶端的磁盤上的,雖然經過加密,但是也有被破解和盜用的風險
- 每次請求都需要附帶cookie,cookie數據較多時造成較大的網絡開銷
就像你把現金放在家里每次買東西都必須帶着現金去一樣
為了解決上面的問題,而出現了session
Session
Session(會話),字面上理解,它是一個抽象的概念,描述瀏覽器和服務器在一段時間內的所有交互動作
本質上:
-
session用於保存與瀏覽器進程對應的數據
-
數據存儲在服務器內存中,具有時效性,默認為30分鍾,可通過代碼或web.xml修改
-
服務器從Cookie中獲取sessionID來找到對應的session對象,從而實現維持用戶的狀態
-
session在服務器是一個Java對象,可以添加任何類型的屬性到其中
-
當session在一段時間內沒有被訪問時將被銷毀
圖解:
注意:如果請求的是JSP將自動創建session對象
session常用方法
方法名稱 | 作用 |
---|---|
session.setAttribute(key,value); | 為session增加額外的屬性,用於傳值 |
session.getAttribute(key); | 從session中取出指定的屬性 |
request.getSession(boolean); | 獲取當前session對象,如果沒有是否創建新的 |
session.getId(); | 獲取ID |
session.setMaxInactiveInterval(); | 設置最大超時時間單位為秒 |
session.getMaxInactiveInterval(); | 獲取最大超時時間 |
session.getCreationTime(); | 獲取創建時間 |
session.getLastAccessedTime(); | 獲取當前會話最后一次請求時間 |
session.invalidate(); | 使session立即失效 |
利用session來實現登錄狀態同步
處理登錄請求的LoginServlet:
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("/LoginServlet")
public class LoginServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//登陸了驗證邏輯
System.out.println("login success!");
//獲取session並 添加用戶信息到session中
req.getSession().setAttribute("user","張三");
}
}
用於顯示用戶主頁的IndexServlet:
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 javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/IndexServlet")
public class IndexServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//獲取session
HttpSession session = req.getSession();
//取出用戶信息
String name = (String) session.getAttribute("user");
//設置響應編碼並輸出用戶名稱
resp.setContentType("text/html;charset=utf-8");
if(name != null){
resp.getWriter().println("user is :"+name);
}else{
resp.getWriter().println("user not login!");
}
}
}
登錄成功后進入IndexServlet可以看到用戶信息
但是重啟瀏覽器時用戶信息就沒有了
注意:
瀏覽器進程結束時sessionID就消失了,但這並不意味着服務器對應的session對象立即會銷毀,如果沒有調用session.invalidate
方法session將達到超時時間后才會銷毀,這意味着大量的session將導致資源耗盡
web.xml配置超時時間:
JSP內置對象
JSP本質還是Servlet,只是對其進行了簡化封裝,為了方便在JSP中進行操作,JSP提供了經常需要使用到的對象
本質:
打開一個轉移后的java文件,找到79行左右可以看到一個非常熟悉的方法_jspService,盡管前面多了__jsp
上圖中圈出了9個JSP的內置對象,大多數都是在Servlet中見過的,除了下面的
-
page,是當前jsp對象實例的this引用
-
pageContext,頁面上下文,頁面的的內置對象都來自於該對象,它是JSP對象操作web資源對象的中轉站(jsp本身並不是Servlet)
-
exception對象僅在錯誤頁面中可用,即用於展示錯誤信息的頁面
錯誤頁面
當服務器處理過程產生異常或是,請求不正確是,tomcat會顯示自帶的最基礎的錯誤頁面,通常需要進行定制
1.編寫錯誤頁面,利用page指令,將這個jsp設置為錯誤頁面
2.在web.xml中配置錯誤頁面,指定要處理的狀態碼和對應的頁面
3.也可以針對某個特殊異常作處理,優先匹配異常類,然后匹配狀態碼
4.在某個servlet中模擬異常
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
throw new ArrayIndexOutOfBoundsException("索引越界了!");
}
頁面內容:
注意:
400+的錯誤頁面無法使用jsp來完成,因為400+錯誤通常都是HTTP協議問題,請求是不正確的,服務器不會產生任何相關的對象
JSP中的四個作用域
作用域值得就是有效范圍,在概念上與大家知道的局部作用域,全局作用域一個意思,
但是這里不是自定義的作用域,而是jsp已經提供了的作用域
四個作用域:
名稱 | 描述 |
---|---|
page | PageContext頁面上下文,在當前頁面有效 |
request | 對同一個請求有效,(請求轉發是同一個,重定向是新的) |
session | 當前會話有效 |
application | ServletContext,當前web應用程序全局有效 |
用來做什么?
在不同servlet或jsp中切換傳參,是非常常見的操作,而要傳參就必須借助上述4個對象
學習作用域的目的是為了合理的選擇最合適的對象來存儲數據,從而節省服務器資源開銷
作用域范圍圖解
老司機經驗:能用小的就別用大的
EL表達式
使用代碼塊可以利用java代碼,來操作對象的屬性,展示數據等,但是對於簡單頻繁的數據展示操作,使用代碼塊或者表達式都會導致html整體結構變得混亂,
那動作指令呢?,雖然語法近似html標簽,但還是不夠簡潔,於是便有了EL表達式
EL概念
EL(Expression Language) 是為了使JSP寫起來更加簡單。它提供了在 JSP 中簡化表達式的方法,讓Jsp的代碼更加簡化。
簡單的說,EL就是要在不影響頁面結構的情況下沒用簡潔的語法來對作用域中的數據進行存取,並提供了完善的運算符支持,
基本語法:
${對象名稱.屬性} 獲取對象屬性
${對象名稱.方法(參數)} 調用對象方法
${參數1 表達式 參數2} 使用運算符
EL包含的內容:
作用域訪問
自動投影,EL會從4個作用域找自動查找匹配的屬性名稱,從小到大查找
//測試代碼
${requestScope.user="jerry2"}
${applicationScope.user="jerry4"}
${pageScope.user="jerry1"}
${sessionScope.user="jerry3"}
<h1>${user}</h1>
運算符
https://tva1.sinaimg.cn/large/006tNbRwgy1g9tz7scwisj30p606r752.jpg
隱式對象
https://tva1.sinaimg.cn/large/006tNbRwgy1g9tz8lnbslj30qh0gkwik.jpg
注意:在使用EL存取對象屬性時必須保證對象提供了相應的get/set方法
示例:
JSTL
有了EL我們可極大的減少在頁面中插入java代碼塊的情況,但是EL是表達式,這意味着其無法提供流程控制能力,即(分支,循環)
於是在廣大程序員的吐槽中,JAVAEE又推出了JSTL標准
概述:
JSTL(JSP Standard Tag Library,JSP標准標簽庫)是一個不斷完善的開放源代碼的JSP標簽庫,是由apache的jakarta小組來維護的。其使命是使用標簽來替換JAVA的流程控制,最終實現JSP中完全或盡量不包含JAVA代碼
注意:如果要使用JSTL,則必須將jstl.jar和 standard.jar文件放到classpath中。
下載地址:
http://archive.apache.org/dist/jakarta/taglibs/standard/binaries/
IDEA下配置步驟:
使用案例:
<%--
Created by IntelliJ IDEA.
User: jerry
Date: 2019/12/12
Time: 4:12 下午
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%--輸出1-10之間的偶數
st表示循環狀態 可獲取當前循環的索引等信息
var 表示每次遍歷的臨時變量
--%>
<c:forEach begin="1" end="10" varStatus="st" var="num">
<c:if test="${st.index % 2 ==0}">
<h1>${num}是偶數啊</h1>
</c:if>
</c:forEach>
<%--重定向--%>
<%--<c:redirect url="404.html"></c:redirect>--%>
<%--輸出--%>
<c:out value="hello world"></c:out>
<%--添加屬性到某個scope--%>
<c:set scope="request" var="number" value="a"></c:set>
<%--選擇結構--%>
<c:choose >
<c:when test="${number=='as'}">
<c:out value="你選擇a了"/>
</c:when>
<c:when test="${number=='b'}">
<c:out value="你選擇b了"/>
</c:when>
<c:otherwise>
<c:out value="不是a和b"/>
</c:otherwise>
</c:choose>
</body>
</html>
以后就用JSP做動態頁面嗎?NO
看看動態頁面的技術發展吧