Jakarta EE 程式設計/Jakarta Server Pages 語法
The JavaServer Pages 是一種技術,用於使用 Java servlet 容器將動態內容插入 HTML 或 XML 頁面。換句話說,您可以傳送 HTML 頁面到 web 客戶端,這些頁面對於每個客戶端來說都是不同的,並且每次他們收到它時都是不同的(例如使用資料庫資料)。為了解釋它,讓我們在一個 web 伺服器上有一個簡單的 HTML 頁面。資料流很簡單。簡而言之,客戶端透過傳送頁面 URL 來請求一個 HTML 頁面,伺服器返回給定的 HTML 頁面
| 客戶端 | 伺服器 | |||
| URL | ||||
| 檢索 該 檔案 | ||||
以下是 HTML 檔案和客戶端上的顯示
| HTML 頁面 | 顯示 |
<html>
<body>
<span style="color: blue;">The current time</span>
</body>
</html>
|
當前時間 |
返回的頁面始終相同。現在我們想要顯示當前時間。為此,我們在 web 伺服器上添加了一個 servlet 容器。HTML 頁面將不再存在於伺服器上。當頁面被客戶端請求時,一個 Java 類將生成 HTML 頁面,並且 HTML 頁面將被髮送到客戶端。這個 Java 類被稱為一個 *servlet*
| 客戶端 | Web 伺服器 | Servlet 容器 | |||||
| URL | |||||||
| 詢問 到 生成 該 檔案 |
|||||||
| 執行 該 Java 類 |
|||||||
以下是 Java 類、生成的 HTML 檔案和客戶端上的顯示
| Servlet | HTML 頁面 | 顯示 |
package jsp_servlet;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
class _myservlet implements javax.servlet.Servlet, javax.servlet.jsp.HttpJspPage {
public void _jspService(javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
throws javax.servlet.ServletException,
java.io.IOException {
javax.servlet.ServletConfig config = …; // Get the servlet config
Object page = this;
PageContext pageContext = …; // Get the page context for this request
javax.servlet.jsp.JspWriter out = pageContext.getOut();
HttpSession session = request.getSession(true);
try {
out.print("<html>\r\n");
out.print("<body>\r\n");
out.print("<span style="color: blue;">");
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Calendar cal = Calendar.getInstance();
out.print(dateFormat.format(cal.getTime()));
out.print("</span>\r\n");
out.print( "</body>\r\n" );
out.print( "</html>\r\n" );
…
} catch (Exception _exception) {
// Clean up and redirect to error page in <%@ page errorPage="myerror.jsp" %>
}
}
}
|
<html>
|
2024/10/24 14:47:26 |
現在頁面是動態的,但 servlet 很難編碼和閱讀。因此,我們將使用一個 *JavaServer Page* (JSP)。JSP 寫起來像 HTML 頁面一樣。它具有用於 HTML 頁面的副檔名 `。jsp` 和用於 XML 標記頁面的副檔名 `。jspx`。除了 HTML 語法之外,它還嵌入了 *指令碼片段* 和 *jsp 標籤*。指令碼片段是 Java 程式碼的一部分。servlet 容器從 JSP 生成一個 servlet,該 servlet 將用於生成 HTML 頁面或 XML 內容。HTML 標記將保持原樣。嵌入的指令碼片段被插入到 servlet 的程式碼中。jsp 標籤被轉換為 Java 程式碼。因此,讓我們使用 JSP
| 客戶端 | Web 伺服器 | Servlet 容器 | |||||
| URL | |||||||
| 詢問 到 生成 該 檔案 |
|||||||
| 生成 該 servlet | |||||||
| 執行 該 Java 類 | |||||||
實際上,servlet 僅在第一次請求呼叫時由 JSP 生成。之後會重複使用相同的 servlet。當 JSP 發生變化時,它會被再次生成。現在我們有一個 JSP,它生成一個 servlet,它生成一個 HTML 頁面,該頁面將顯示給客戶端
| JSP | Servlet | HTML 頁面 | 顯示 |
<%@ page errorPage="myerror.jsp" %>
<%@ page import="java.text.DateFormat" %>
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page import="java.util.Calendar" %>
<html>
<body>
<% DateFormat dateFormat =
new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Calendar cal = Calendar.getInstance();
%>
<span style="color: blue;">
<%= dateFormat.format(cal.getTime()) %>
</span>
</body>
</html>
|
package jsp_servlet;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
class _myservlet implements javax.servlet.Servlet,
javax.servlet.jsp.HttpJspPage {
public void _jspService(
javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
throws javax.servlet.ServletException,
java.io.IOException {
javax.servlet.ServletConfig config = …;
Object page = this;
PageContext pageContext = …;
javax.servlet.jsp.JspWriter out =
pageContext.getOut();
HttpSession session = request.getSession(true);
try {
DateFormat dateFormat =
new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Calendar cal = Calendar.getInstance();
out.print("<html>\r\n");
out.print("<body>\r\n");
out.print("<span style="color: blue;">\r\n");
out.print(dateFormat.format(cal.getTime()));
out.print("\r\n");
out.print("</span>\r\n");
out.print( "</body>\r\n" );
out.print( "</html>\r\n" );
…
} catch (Exception _exception) {
}
}
}
|
<html>
|
2024/10/24 14:47:26 |
有 *三種* 基本的指令碼元素型別,允許將 java 程式碼直接插入到 JSP 中。
- 一個 *宣告* 標籤將變數定義放在 java servlet 類的主體內部。也可以定義靜態資料成員。還可以在這裡定義內部類。
<%! int serverInstanceVariable = 1; %>
宣告標籤還允許定義方法。
<%!
/**
* Converts the Object into a string or if
* the Object is null, it returns the empty string.
*/
public String toStringOrBlank(Object obj) {
if (obj != null) {
return obj.toString();
}
return "";
}
%>
- 一個 *指令碼片段* 標籤將它包含的所有語句放在 java servlet 類中 `_jspService()` 方法的內部。
<% int localStackBasedVariable = 1; out.println(localStackBasedVariable); %>
- 一個 *表示式* 標籤將要計算的表示式放在 java servlet 類的內部。表示式不應以分號結尾。
<%= "expanded inline data " + 1 %>
- 一個 *註釋* 標籤什麼也不做。它被忽略了。它允許您對檔案進行文件化。它與 HTML 註釋不同,因為 HTML 註釋 (
<!-- -->) 將出現在生成的 HTML 檔案中。
<%-- This is my first JSP. --%>
JSP 指令新增到 JSP 頁面的頂部。這些指令控制 JSP 編譯器如何生成 servlet。以下指令可用
- 包含
- include 指令通知 JSP 編譯器將一個完整的檔案包含到當前檔案中。就像將包含檔案的內容直接貼上到原始檔案中一樣。此功能類似於 C 預處理器提供的功能。包含的檔案通常具有副檔名“jspf”(代表 JSP **片段**)
<%@ include file="somefile.jspf" %>
- 頁面
- page 指令具有幾個屬性
匯入 |
導致在結果檔案中插入一個 Java `import` 語句。 |
contentType |
指定生成的內容。如果未使用 HTML 或字元集不是預設字元集,則應使用它。 |
errorPage |
指示在處理 HTTP 請求時發生異常時應該顯示的頁面的地址。 |
isErrorPage |
如果設定為 true,則表示這是錯誤頁面。預設值為 *false*。 |
isThreadSafe |
如果結果 servlet 必須是執行緒安全的,則為 true。 |
autoFlush |
自動重新整理內容。值為 true(預設值)表示緩衝區已滿時應重新整理緩衝區。值為 false(很少使用)表示緩衝區溢位時應丟擲異常。當也使用 buffer="none" 時,值為 false 是非法的。 |
會話 |
為了維護會話。值為 true(預設值)表示預定義變數 session(型別為 `HttpSession`)應繫結到現有會話(如果存在),否則應建立一個新會話並將其繫結到它。值為 false 表示不使用任何會話,嘗試訪問變數 session 將導致在 JSP 頁面被翻譯成 servlet 時出現錯誤。 |
緩衝區 |
設定緩衝區大小。預設值為 8k,建議您增加它。 |
isELIgnored |
定義當 JSP 被翻譯時是否忽略 表示式語言 (EL) 表示式。 |
語言 |
定義指令碼片段、表示式和宣告中使用的指令碼語言。目前,唯一可能的值是“java”。 |
擴充套件 |
定義此 JSP 將成為的類的超類。除非您 *確實* 知道自己在做什麼,否則不要使用它 - 它會覆蓋容器提供的類層次結構。 |
資訊 |
定義一個字串,該字串被放入翻譯後的頁面中,以便您可以使用生成的 servlet 的繼承的 `getServletInfo()` 方法獲取它。 |
pageEncoding |
定義 JSP 的字元編碼。預設值為“ISO-8859-1”(除非 contentType 屬性已定義字元編碼,或者頁面使用 XML 文件語法)。 |
<%@ page import="java.util.*" %> <%-- example import --%> <%@ page contentType="text/html" %> <%-- example contentType --%> <%@ page isErrorPage="false" %> <%-- example for non error page --%> <%@ page isThreadSafe="true" %> <%-- example for a thread safe JSP --%> <%@ page session="true" %> <%-- example for using session binding --%> <%@ page autoFlush="true" %> <%-- example for setting autoFlush --%> <%@ page buffer="20kb" %> <%-- example for setting Buffer Size --%>
- **注意:** 只有“import”頁面指令可以在同一個 JSP 中多次使用。
- taglib
- taglib 指令表示要使用 JSP 標籤庫。該指令需要一個字首(類似於 C++ 中的名稱空間)和標籤庫描述的 URI。
<%@ taglib prefix="myprefix" uri="taglib/mytag.tld" %>
無論 JSP 編譯器是否為 servlet 生成 Java 原始碼或直接發出位元組碼,瞭解 JSP 編譯器如何將頁面轉換為 Java servlet 都是有幫助的。例如,考慮以下輸入 JSP 及其生成的 Java Servlet。
| 輸入 JSP | 生成的 servlet |
<%@ page errorPage="myerror.jsp" %>
<%@ page import="com.foo.bar" %>
<html>
<head>
<%! int serverInstanceVariable = 1;%>
<% int localStackBasedVariable = 1; %>
</head>
<body>
<table>
<%-- This will be ignored. --%>
<tr><td><%= toStringOrBlank( "expanded inline data " + 1 ) %></td></tr>
</table>
</body>
</html>
|
package jsp_servlet;
import java.util.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import com.foo.bar; // Imported as a result of <%@ page import="com.foo.bar" %>
import …
class _myservlet implements javax.servlet.Servlet, javax.servlet.jsp.HttpJspPage {
// Inserted as a
// result of <%! int serverInstanceVariable = 1;%>
int serverInstanceVariable = 1;
…
public void _jspService(javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
throws javax.servlet.ServletException,
java.io.IOException {
javax.servlet.ServletConfig config = …; // Get the servlet config
Object page = this;
PageContext pageContext = …; // Get the page context for this request
javax.servlet.jsp.JspWriter out = pageContext.getOut();
HttpSession session = request.getSession(true);
try {
out.print("<html>\r\n");
out.print("<head>\r\n");
…
// From <% int localStackBasedVariable = 1; %>
int localStackBasedVariable = 1;
…
out.print("</head>\r\n");
out.print("<body>\r\n");
out.print("<table>\r\n");
out.print(" <tr><td>");
// From <%= toStringOrBlank( "expanded inline data " + 1) %>
out.print(toStringOrBlank( "expanded inline data " + 1 ));
out.print(" </td></tr>\r\n");
out.print("</table>\r\n");
out.print("</body>\r\n");
out.print("</html>\r\n");
…
} catch (Exception _exception) {
// Clean up and redirect to error page in <%@ page errorPage="myerror.jsp" %>
}
}
}
|
我們已經看到,我們可以在指令碼中宣告物件,供以後使用。還有一些已經宣告的物件可供程式設計師使用。它們被稱為*隱式物件*。
out |
用於將資料寫入響應流的JspWriter。 |
頁面 |
servlet 本身。 |
pageContext |
包含與整個頁面相關聯的資料的PageContext 例項。給定的 HTML 頁面可以在多個 JSP 之間傳遞。 |
request |
提供 HTTP 請求資訊的HttpServletRequest 物件。 |
response |
可用於將資料傳送回客戶端的HttpServletResponse 物件。 |
會話 |
可用於跟蹤從一個請求到另一個請求的使用者資訊的HttpSession 物件。 |
config |
提供 servlet 配置資料。 |
application |
應用程式中所有 JSP 和 servlet 共享的資料。 |
exception |
應用程式程式碼未捕獲的異常。 |
JSP 操作是 XML 標記,用於呼叫內建的 Web 伺服器功能。它們在執行時執行。有些是標準的,有些是自定義的(由 Java 開發人員開發)。以下是標準操作。
與 include 指令類似,此標記將指定的 jsp 包含到返回的 HTML 頁面中,但其工作原理不同。Java servlet 會暫時將請求和響應傳遞給指定的 JavaServer 頁面。一旦另一個 JSP 完成,控制權將返回到當前 JSP。使用此方法,JSP 程式碼將在多個其他 JSP 之間共享,而不是重複。
<html>
<head></head>
<body>
<jsp:include page="mycommon.jsp" >
<jsp:param name="extraparam" value="myvalue" />
</jsp:include>
name:<%=request.getParameter("extraparam")%>
</body>
</html>
可以在jsp:include、jsp:forward 或jsp:params 塊中使用。指定將新增到請求當前引數的引數。
用於將請求和響應傳遞給另一個 JSP 或 servlet。控制權永遠不會返回到當前 JSP。
<jsp:forward page="subpage.jsp" >
<jsp:param name="forwardedFrom" value="this.jsp" />
</jsp:forward>
在此轉發示例中,請求將轉發到subpage.jsp。
舊版本的 Netscape Navigator 和 Internet Explorer 使用不同的標記來嵌入小程式。此操作生成瀏覽器特定的標記,用於包含小程式。外掛示例說明了在網頁中嵌入小程式的 HTML 統一方式。在<OBJECT> 標記出現之前,沒有通用的嵌入小程式的方式。
<jsp:plugin type=applet height="100%" width="100%"
archive="myjarfile.jar, myotherjar.jar"
codebase="/applets"
code="com.foo.MyApplet" >
<jsp:params>
<jsp:param name="enableDebug" value="true" />
</jsp:params>
<jsp:fallback>
Your browser does not support applets.
</jsp:fallback>
</jsp:plugin>
目前,jsp:plugin 標記不允許動態呼叫小程式。例如,jsp:params 不能與需要將資料點作為引數傳遞的圖表小程式一起使用,除非資料點的數量是恆定的。例如,您不能遍歷 ResultSet 來建立jsp:param 標記。每個jsp:param 標記都必須手動編碼。
如果瀏覽器不支援小程式,則顯示的內容。
從指定的 JavaBean 獲取屬性。
除了預定義的 JSP 操作之外,開發人員還可以使用 JSP 標記擴充套件 API 新增自己的自定義*操作*。開發人員編寫一個 Java 類,該類實現其中一個 Tag 介面,並提供一個標記庫 XML 描述檔案,該檔案指定標記和實現這些標記的 java 類。
考慮以下 JSP。
<%@ taglib uri="mytaglib.tld" prefix="myprefix" %> … <myprefix:myaction> <%-- The start tag --%> … </myprefix:myaction> <%-- The end tag --%> …
JSP 編譯器將載入 mytaglib.tld XML 檔案。
<taglib>
<tlib-version>1.0</tlib-version>
<jsp-version>2.0</jsp-version>
<short-name>My taglib</short-name>
<tag>
<name>myaction</name>
<tag-class>org.wikibooks.en.MyActionTag</tag-class>
<body-content>empty</body-content>
</tag>
</taglib>
JSP 編譯器將看到標記myaction 由 java 類MyActionTag 實現。
public class MyActionTag extends TagSupport {
public MyActionTag() { … }
// Releases all instance variables.
public void release() { … }
// Called for the start tag
public int doStartTag() { … }
// Called at the end tag
public int doEndTag() { … }
}
標記在檔案中首次使用時,它將建立一個MyActionTag 例項。然後(以及每次使用該標記時),當它遇到開始標記時,它將呼叫方法doStartTag()。它檢視開始標記的結果,並確定如何處理標記的主體。主體是開始標記和結束標記之間的文字。doStartTag() 方法可以返回以下值之一
SKIP_BODY |
不會處理標記之間的主體。 |
EVAL_BODY_INCLUDE |
評估標記的主體。 |
EVAL_BODY_TAG |
評估標記的主體並將結果推送到流中(儲存在標記的主體內容屬性中)。 |
注意:如果標記擴充套件了BodyTagSupport 類,則在呼叫doEndTag() 之前,將在主體處理完成後呼叫方法doAfterBody()。此方法用於實現迴圈結構。
當它遇到結束標記時,它將呼叫doEndTag() 方法。該方法可以返回以下兩個值之一
EVAL_PAGE |
這表示應處理 JSP 檔案的其餘部分。 |
SKIP_PAGE |
這表示不應進行任何進一步的處理。控制權離開 JSP 頁面。這就是用於轉發操作的方法。 |
如果要迭代主體幾次,則 Java 類(標記處理程式)必須實現IterationTag 介面。它返回EVAL_BODY_AGAIN - 表示再次呼叫主體。
JavaServer Pages 標準標記庫 (JSTL) 是 Java EE Web 應用程式開發平臺的一個元件。它透過新增 JSP 標記的標記庫來擴充套件 JSP 規範,這些標記庫用於常見任務,例如 XML 資料處理、條件執行、迴圈和國際化。
Struts 專案包含Struts 標記庫,其中大部分在獨立於 Struts 架構的情況下很有用。
JSP 中的國際化與普通 Java 應用程式中的國際化方式相同,即使用資源束。
表示式語言
[edit | edit source]表示式語言 (EL) 是一個更快/更簡單的顯示引數值的方式。它自 JSP 2.0 起可用。例如,它允許開發者建立 Velocity 風格的模板。
Hello, ${param.visitor}
與以下程式碼相同
Hello, <%=request.getParameter("visitor")%>
它還提供了一種更清晰的方式來遍歷巢狀 bean。考慮一些 bean
class Person {
String name;
// Person nests an organization bean.
Organization organization;
public String getName() { return this.name; }
public Organization getOrganization() { return this.organization; }
}
class Organization {
String name;
public String getName() { return this.name; }
}
然後,如果一個 Person 例項要被放置到一個名為 "person" 的請求屬性中,JSP 將有
Hello, ${person.name}, of company ${person.organization.name}
與以下程式碼相同。
Hello,
<% Person p = (Person) request.getAttribute("person");
if (p != null) {
out.print(p.getName());
}
%>, of company
<% if ((p != null) && (p.getOrganization() != null)) {
out.print(p.getOrganization().getName());
}
%>
Java EE 5 平臺中的 JSP 技術
[edit | edit source]Java EE 5 的重點是透過使用 Java 語言註解來簡化開發,這些註解是在 J2SE 5.0 中引入的。JSP 2.1 透過定義用於 JSP 標籤處理程式和上下文監聽器的依賴注入的註解來支援這個目標。
Java EE 5 規範的另一個關鍵關注點是使其 Web 層技術(即 JavaServer Pages (JSP)、JavaServer Faces (JSF) 和 JavaServer Pages Standard Tag Library (JSTL))保持一致。
這種對齊工作的成果是統一表達式語言 (EL),它集成了 JSP 2.0 和 JSF 1.1 定義的表示式語言。
統一 EL 中從對齊工作中產生的主要關鍵補充是:一個可插拔的 API,用於將變數引用解析為 Java 物件,以及用於解析應用於這些 Java 物件的屬性;對延遲表示式的支援,這些表示式可以在需要時由標籤處理程式進行評估,不同於它們的常規表示式對應物,這些表示式在頁面執行和渲染時立即進行評估;對 lvalue 表示式的支援,這些表示式出現在賦值操作的左側。當用作 lvalue 時,EL 表示式代表對資料結構(例如:JavaBeans 屬性)的引用,該資料結構被分配了一些使用者輸入。統一 EL 在其自身的規範文件中定義,該文件與 JSP 2.1 規範一起提供。
由於統一 EL 的存在,JSTL 標籤(例如 JSTL 迭代標籤)可以以直觀的方式與 JSF 元件一起使用。
JSP 2.1 利用 Servlet 2.5 規範來實現其 Web 語義。
模型-檢視-控制器模式
[edit | edit source]
模型-檢視-控制器模式可以與 JSP 檔案一起使用,以便將表示與請求處理和計算機資料儲存分離。使用常規 servlet 或單獨的 JSP 檔案來處理請求。請求處理完成後,控制權將傳遞給僅用於建立輸出的 JSP。有幾個基於模型-檢視-控制器模式的平臺用於 Web 層(如 Barracuda、Apache Struts、Stripes 和 Spring MVC 框架)。
環境
[edit | edit source]Java 伺服器 (J2EE 規範) 和頁面指令碼和/或新增的擴充套件定製程式設計都透過 (在被載入的程式的執行時上下文中使用) 一個稱為虛擬機器的預安裝基礎程式與主機作業系統整合,這種型別是 Java 虛擬機器 (JVM)。
因為無論是編譯器-JVM 集 (稱為 SDK 或 JDK) 還是獨立的 JVM (稱為 JRE,Java 執行時環境) 都是為大多數計算機平臺作業系統而製作的,並且為 JVM 編譯的程式被編譯成 JVM 的特殊 Java 位元組碼檔案,位元組碼檔案 (編譯的 Java 程式 .class 檔案) 可以有效地在平臺之間傳輸,無需重新編譯,除非版本相容性或特殊情況。這些 J2EE servlet 或 J2EE JSP 程式的原始碼幾乎總是與 J2EE JSP 材料和 J2EE Web 應用程式一起提供,因為伺服器在載入它們時必須呼叫編譯器。這些小的擴充套件程式 (自定義標籤、servlet、bean、頁面指令碼) 是可變的,並且很可能在執行時之前或間歇性地更新或更改,但特別是在傳送 JSP 頁面請求時,它需要 JSP 伺服器訪問 Java 編譯器 (SDK 或 JDK) 和所需的原始碼 (不僅僅是 JVM JRE 和位元組碼類檔案) 才能成功利用服務方法。
JSP 語法有兩種基本形式,指令碼和標記,儘管從根本上說,頁面是 HTML 或 XML 標記。指令碼標記 (稱為指令碼元素) (分隔) 程式碼塊 與標記不是有效的標記,並且允許任何與 java 伺服器相關的 API (例如,執行的二進位制檔案本身或資料庫連線 API 或 java 郵件 API) 或更專業的 JSP API 語言程式碼被嵌入到 HTML 或 XML 頁面中,前提是 JSP 檔案中使用了正確的宣告,並且使用了頁面的副檔名。指令碼塊不需要在塊本身內完成,只需要塊本身的最後一行在語法上正確地作為語句完成,它可以在後面的塊中完成。這種分割的內聯程式碼部分系統被稱為跨越指令碼,因為它可以透過跨越靜態標記來進行包裝。在執行時 (在客戶端請求期間),程式碼被編譯和評估,但程式碼的編譯通常只發生在對檔案程式碼進行更改時。



