JSP
尚硅谷JavaWeb筆記-05
JSP
Java Server Page
- 接收請求、處理請求已經了解,現在來到響應的部分
- 當有人來請求,我們用servlet接收並動態回應,比如想輸出一個html頁面作為結果,servlet可以做到但實現的辦法很蠢,就是靠
out.print
拼接硬刻出標籤文本格式,改進辦法就是JSP - JSP將
java > html
的過程包裝起來,可以更好的處理View的部分- 底層代碼其實也是拼接,不過Java就是這樣,一層套一層
- 轉換過程
a.jsp >(被web容器翻譯成servlet) > a_jsp.java > a_jsp.class
- 然後類似一個servlet的生命週期,不過jsp轉來的有自己的方法名
構造 > _jspInit() > _jspService() > _jspDestroy()
JSP與Servlet的異同
- 兩者都是HttpServlet(Servlet API)的子類
URL對應
- Servlet的URL對應是需要設定的
- 但是JSP的對應就是它的實體路徑
如何被載入
- Servlet一開始就是被編譯成class檔案,然後被http request的時候在被Web Container掛載進來
- JSP是在被第一次呼叫的時候才會被Web Container先翻譯成為Servlet的java寫法,才編譯成為class檔案,放在Web Container一個 暫時的資料夾。雖然第一次執行會比Servlet慢,不過掛載以後就一樣了(因為JSP和Servlet一樣,只會掛載一次)
更新方式
- Servlet如果有修改,需要重新編譯,因此需要重啟Web Container的服務
- JSP頁面因為Web Container有在監控,因此,只要有修改,他會重新翻譯、編譯然後掛載。因此不需要重啟Web Container就能看到最新修改
JSP過氣?
到了2021年現在技術已經傾向前後端分離,JSP這種從後端響應結果到前端的技術確實已經少用了,但學javaweb作為基礎知識還是很有必要的,畢竟框架例如springMVC也是基於這些技術搭建,夯實地基才能建高樓
檔頭聲明
<!-- 這是 jsp 檔的頭聲明。表示這是 jsp 頁面 -->
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
language
:值只能是 java。 表示翻譯的得到的是 java 語言的contentType
:設置回應頭 contentType 的內容pageEncoding
:設置當前 jsp 頁面的編碼import
:給當前jsp 頁面導入需要使用的類包autoFlush
:設置是否自動刷新out 的緩衝區,預設為truebuffer
:設置out 的緩衝區大小。預設為8KBerrorPage
:設置當前jsp 發生錯誤後,需要跳轉到哪個頁面去顯示錯誤資訊isErrorPage
:設置當前jsp 頁面是否是錯誤頁面。是的話,就可以使用exception 異常物件session
:設置當前jsp 頁面是否獲取session 物件,預設為trueextends
:給伺服器廠商預留的jsp 預設翻譯的servlet 繼承於什麼類
代碼格式
- 要啟動一個jsp頁面,需要編寫.jsp檔案,在IDEA中可以用右鍵新增會自動導入預設格式
- 然後要記住運行jsp是需要web容器的協助,啟動tomcat後,在url從工程路徑找到它
- 如果出現錯誤或方法不給用,可能是沒導包,專案沒啟動前當然不認得你的jsp方法,到
apache-tomcat-8.0.50\lib\
找jsp-api.jar
自己手動導入,就可以開始用了
聲明腳本(極少用)
近乎廢棄,看看就好
格式 <%! 聲明 java 代碼 %>
<%!
private Integer id;
private String name;
private static Map<String,Object> map;
%>
運算式腳本(常用)
- 所有的運算式腳本都會被翻譯到_jspService()方法中
- 運算式腳本都會被翻譯成out.printf()輸出到頁面上
- 由于運算式腳本翻譯的內容都在_jspService()方法中,所以_jspService()方法中的物件都可以直接使用
- 不能以分號結束
格式 <%=運算式%>
<%=12 %> <br>
<%=12.12 %> <br>
<%="我是字串" %> <br>
<%=map%> <br>
<%=request.getParameter("username")%>
代碼腳本
- 特性同運算式腳本
- 優勢在於可以把java跟html混著用(但這樣很鬧,不利於閱讀)
格式
<%
java 語句
%>
<table border="1" cellspacing="0">
<%
for (int j = 0; j < 10; j++) {
%>
<tr>
<td>第 <%=j + 1%>行</td>
</tr>
<%
}
%>
</table>
註釋
- JSP的註釋有三種,但有些會保留到翻譯或轉譯後,需要看情況使用
<!-- 這是 html 注釋 -->
html注釋會被翻譯到java原始程式碼中
在_jspService方法裡以out.writer輸出到用戶端,在.html檔中能見
<%
// 單行 java 注釋
/* 多行 java 注釋 */
%>
java注釋會被翻譯到java原始程式碼中,在.java檔中能見
<%-- 這是 jsp 注釋 --%>
jsp注釋才是真正只在jsp中
四大域物件
jsp既然跟servlet一樣,自然內建有request或response等物件提供http協議通訊。也有可以用來儲存上下文或一些資料的Context物件
- 他們的使用範圍從小到大是:
- pageContext (PageContextImpl 類):當前 jsp 頁面範圍內有效
- request (HttpServletRequest 類):一次請求內有效
- session (HttpSession 類):一個會話範圍內有效,打開流覽器訪問伺服器,直到關閉流覽器
- application (ServletContext 類):整個 web 工程範圍內都有效,只要 web 工程不停止,資料都在
- 使用時當然優先從小的開始用,不行再增加範圍,以節省資源
<body>
<h1>scope.jsp 頁面</h1>
<%
// 往四個域中都分別保存了資料
pageContext.setAttribute("key", "pageContext");
request.setAttribute("key", "request");
session.setAttribute("key", "session");
application.setAttribute("key", "application");
%>
pageContext 域是否有值:<%=pageContext.getAttribute("key")%> <br>
request 域是否有值:<%=request.getAttribute("key")%> <br>
session 域是否有值:<%=session.getAttribute("key")%> <br>
application 域是否有值:<%=application.getAttribute("key")%> <br>
<%
request.getRequestDispatcher("/scope2.jsp").forward(request,response);
%>
</body>
-----------------------
<body>
<h1>scope2.jsp 頁面</h1>
pageContext 域是否有值:<%=pageContext.getAttribute("key")%> <br>
request 域是否有值:<%=request.getAttribute("key")%> <br>
session 域是否有值:<%=session.getAttribute("key")%> <br>
application 域是否有值:<%=application.getAttribute("key")%> <br>
</body>
輸出
- 想從jsp輸出至html,有3種方法:
out.write()
:會輸出字節流,可能有亂碼問題out.print()
:在底層代碼還是轉成out.write,不過用它可以避免亂碼response.getWriter().write()
:會放在response緩衝區,它的優先順位比較高
- 避免混亂最好統一使用
out.print()
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
response.getWriter().write("寫到html的第1行 <br/>");
out.write("寫到html的第2行 <br/>");
out.print("寫到html的第3行 <br/>");
%>
</body>
</html>
包含標籤
- 包含就是Servlet容器將其他Web組件(JSP、Servlet、Html),將生成的結果包含到自己的結果中
- 比如用來把網站模組化拆開實現解偶與復用,比如網頁腳部訊息(看網站時拉到最下面聯絡資訊那些),讓他引用同一個jsp,我只要維護這一個jsp就可以套用到整個網站的所有頁面
Servlet之間是不允許相互調用的,所以只能用jsp實現
靜態包含
- 格式:
<%@ include file="/xxx/footer.jsp"%>
注意第一個/表示http://ip:port/工程/
- 不翻譯,直接把jsp代碼原封不動拷貝一份過去
- 屬於編譯階段代碼的拼接,最終只會產生一個.class文件
- 用於加載後就再也不會變的東西
動態包含
已經較少使用了,原因後述
- 格式:
<jsp:include page="/include/footer.jsp">
<jsp:param name="username" value="bbj"/>
<jsp:param name="password" value="root"/>
</jsp:include>
- 可以傳遞參數
- 當web容器執行到include語句才開始翻譯要引用的內容,產生出對應的Servlet文件並調用執行(最終會產生多個.class文件),所以達到動態的效果
- 屬於編譯後,servlet運行結果的拼接
- 用於加載比如查詢資料庫結果、時間戳等等
轉發標籤
- 格式:
<jsp:forward page="/scope2.jsp"></jsp:forward>
- 就是用來轉發,跟寫這個有一樣的效果
<%
request.getRequestDispatcher("/scope2.jsp").forward(request,response);
%>
小結
前端收到一個請求 > Servlet接收 > 處理(調用資料庫等等) > 將參數(可能是查詢的結果)存在域物件中(因為是同一個request) > 轉發給JSP > 從域物件解析參數 > 生成html傳給前端
JSP的痛點
引用 https://www.zhihu.com/question/328713931/answer/711014242
- 動態資源和靜態資源全部耦合在一起,無法做到真正的動靜分離。伺服器壓力大,因為伺服器會收到各種http請求,例如css的http請求,js的圖片的,動態代碼的等等。一旦伺服器出現狀況,前後臺一起玩完,用戶體驗極差。
- 前端工程師做好html後,需要由java工程師來將html修改成jsp頁面,出錯率較高(因為頁面中經常會出現大量的js代碼),修改問題時需要雙方協同開發,效率低下。
- jsp必須要在支援java的web伺服器裡運行(例如tomcat等),無法使用nginx等(nginx據說單實例http併發高達5w,這個優勢要用上),性能提不上來。
- 第一次請求jsp,必須要在web伺服器中編譯成servlet,第一次運行會較慢。
- 每次請求jsp都是訪問servlet再用輸出流輸出的html頁面,效率沒有直接使用html高。
- jsp內有較多標籤和運算式,前端工程師在修改頁面時會捉襟見肘,遇到很多痛點。
- 如果jsp中的內容很多,頁面回應會很慢,因為是同步載入。
上次修改於 2022-01-05