編寫Servlet代碼的時候,向響應中輸出HTML文檔是很是不方便的。html
PrintWriter writer = response.getWriter(); writer.append("<!DOCTYPE html>\r\n") .append("<html>\r\n") .append(" <head>\r\n") .append(" <title>Hello World Application</title>\r\n") .append(" </head>\r\n") .append(" <body>\r\n") .append(" Nick says, \"Hello, World!\"\r\n") .append(" </body>\r\n") .append("</html>\r\n");
在普通的HTML文件中編寫上面代碼的返回內容是很是簡單的。java
<!DOCTYPE html> <html> <head> <title>Hello World Application</title> </head> <body> Nick says, "Hello, World!" </body> </html>
因而,Java EE規範的建立者就設計了JavaServer Pages(也稱爲JSP)用於知足這個需求。數據庫
JSP結合了Java代碼和HTML標籤。JSP能夠包含除了Java代碼以外的任何HTML標籤、內建的JSP標籤、自定義JSP標籤以及表達式語言。apache
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <!DOCTYPE html> <html> <head> <title>Hello World Application</title> </head> <body> Hello, World! </body> </html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
將設置頁面的內容類型和字符編碼。以前咱們使用HttpServletResponse
的setContentType
和setCharacterEncoding
方法進行設置。該JSP中的其餘內容都只是普通的HTML,將做爲響應內容被髮送到客戶端。編程
JSP實際上只是一個精心設計的Servlet。
在編譯Java代碼的時候,它將被轉換成字節碼。重要的是咱們將使用字節碼而不是Java代碼。字節碼並非對Java程序最終的渲染。字節碼仍然是獨立於平臺的,並不足以運行在各類不一樣的操做系統上。
當Java在JRE中運行時,即時編譯器將把它編譯成機器碼(特定於運行JRE的目標機器)。最終執行的實際是機器碼。
在運行時,JSP代碼將由JSP編譯器進行轉換,它將解析出JSP代碼的全部特性,並將它們轉換成Java代碼。由JSP建立獲得的Java類都將實現Servlet。
在IDE中編譯hello-world-jsp項目,啓動調試器並打開瀏覽器訪問http://localhost:8080/hello-world-jsp/。在Tomcat的主目錄下的work\Catalina\localhost\hello-world-jsp\org\apache\jsp中,能夠看到編譯過的JSP文件和生成的中間Java文件。
瀏覽器
各個Web容器生成的JSP Servlet類看起來並不一致。JSP編譯後的類最終取決於它在其中運行的Web容器。重要的一點是:JSP的行爲和語法有標準規範,只要使用的Web容器兼容於該規範,那麼JSP在全部容器中都將有着相同的行爲,即便它們編譯生成的代碼可能不盡相同。session
JSP就像普通的Servlet同樣,能夠在運行時進行調試(IntelliJ IDEA能夠在任何JSP代碼中添加斷點,Eclipse只容許在JSP的內嵌Java代碼中添加斷點)。
在某些容器(例如Tomcat)中,JSP將在第一次請求到達時被即時轉換並編譯。對於以後的請求,能夠直接使用編譯好的JSP。
JSP能夠改善編程速度、效率和開發過程的準確性,因此它是最好的選擇。app
JSP在執行時有不少事情必須處理,但全部的這些事情都已經被處理了。因此,訪問一個什麼內容都沒有的JSP頁面不會出現任何錯誤,一切都正常工做。
JSP默認的內容類型爲text/html,默認的字符編碼爲ISO-8859-1。不過,默認的字符編碼與許多特殊字符並不兼容,例如非英語的語言,它們可能會影響應用程序的本地化。
jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
轉換後的代碼爲:函數
response.setContentType("text/html;charset=UTF-8"); /* //等同於 response.setContentType("text/html"); response.setCharacterEncoding("charset=UTF-8"); */ //也等同於 //response.setHeader("Content-Type","text/html;charset=UTF-8");
<%@ 這是一個指令 %> <%! 這是一個聲明 %> <% 這是一個腳本 %> <%= 這是一個表達式 %>
聲明用於在JSP Servlet類的範圍內聲明一些東西,例如能夠定義實例變量、方法或聲明標籤中的類。
腳本被複制到_jspService
方法的主體中。該方法中的全部局部變量均可以在腳本中使用。能夠在腳本中使用條件語句、操做對象和執行數學計算。
表達式的做用域與腳本相同,它也將被複制到_jspService
方法中。表達式用於向客戶端輸出一些內容,把代碼的返回值變量輸出到客戶端。在表達式中能夠執行數學計算,還能夠調用一些返回字符串、數字或其餘原生類型的方法。事實上,任何賦值表達式的整個右側均可以用在表達式中。
在JSP中實現代碼註釋的方法有如下4種:
XML註釋<!-- 這是一個HTML/XMl註釋 -->
能夠被髮送到客戶端,重要的是它並未阻止其中Java代碼的執行。
能夠在JSP的聲明和腳本中使用任何合法的Java註釋,包括行註釋和塊註釋。
JSP註釋<%-- 這是一個JSP註釋 --%>
不只不會發送到瀏覽器,甚至連JSP編譯器也不會解釋/轉換它們。若是須要註釋一段包含JSP腳本、表達式、聲明、指令和標記的代碼的話,這是很是有用的。
不管什麼時候在JSP中包含直接使用類的Java代碼,該JSP要麼使用徹底限定類名,要麼在JSP文件中添加一條導入指令。正如在Java文件中,java.lang包中的全部類都將被隱式地導入同樣,它們也會被隱式地導入到JSP文件中。
在JSP中導入Java類的方式:
<%@ page import="java.util.*,java.io.IOException" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" import="java.util.*,java.io.IOException"%>
<%@ page import="java.util.Map" %> <%@ page import="java.util.List" %> <%@ page import="java.io.IOException" %>
pageEncoding指定JSP所使用的字符編碼,等同於HttpServletResponse中的setCharacterEncoding
方法。
在page指令中使用<%@ page contentType="text/html" pageEncoding="UTF-8" language="java" %>
取代以前的<%@ page contentType="text/html;charset=UTF-8" language="java" %>
session的值只能是真和假中的一個,表示JSP是否將參與HTTP會話。默認值爲真,所以在JSP中能夠訪問隱式的session變量。
isELIgnored表示JSP編譯器是否將解析和轉換JSP中的表達式語言(EL)。
若是在JSP的執行過程當中出現了錯誤,errorPage將告訴容器應該將請求轉發到哪一個JSP。
isErrorPage表示當前的JSP是否被用做錯誤頁面(默認值爲假)。
<%@ include file="index.jsp" %>
特性file指定須要包含的JSP文件的路徑。
若是使用的是絕對路徑,容器將從應用程序的Web根目錄開始定位該文件。對於存儲在WEB-INF目錄中的included.jsp文件來講,可使用路徑/WEB-INF/included.jsp包含它。
若是使用的是相對路徑,它將從包含指令的JSP文件所在的目錄開始定位包含文件。
在JSP被轉換成Java以前,編譯器將使用被包含JSP文件的內容替換include指令。所以,該過程是靜態的而且只發生一次。
<jsp:include page="index.jsp" />
是另外一種包含JSP頁面的方式,它經過動態(運行時)的方式包含。它使用的路徑仍然是相對於當前文件的相對路徑,或者從Web根目錄開始的絕對路徑。被包含的文件將會單獨編譯。在運行時,請求將會被臨時地重定向至被包含的JSP,再將該JSP的結果輸出到響應中,而後再將控制權返還給主JSP頁面。
這兩種包含文件的方法各有優劣。指令include速度快,被引用的JSP文件能夠引用主JSP文件中定義的全部變量。但這種方法將使JSP文件變大,記住,Java方法編譯後的字節數目最大不能超過65534字節。
<jsp:include>
在每次頁面加載時都會從新計算,而且被包含的JSP文件不能使用主JSP中已定義的變量。
大多數狀況下,include指令都是最好的選擇。
若是但願在JSP中使用標籤庫中定義的標籤,使用taglib指令引用該標籤庫便可。
<%@ taglib uri="http://java.sum.com/jsp/jstl/core" prefix="c" %>
特性uri指定了目標標籤庫所屬的URI命名空間,特性prefix則定義了用於引用庫中標籤時使用的別名。
經過<jsp:forward page="/some/other/page.jsp" />
標籤能夠將當前JSP正在處理的一些請求轉發至其餘JSP。在該標籤以前生成的任何響應內容仍然會被髮送到客戶端瀏覽器中。任何在此標籤以後的代碼都將被忽略。
<jsp:useBean>
標籤在頁面中聲明一個JavaBean。
<jsp:getProperty>
將從使用<jsp:useBean>
聲明的bean中獲取屬性值。
<jsp:setProperty>
將用於設置該實例的屬性。
<jsp:useBean>
標籤建立的bean能夠經過其餘JSP標籤、JSP腳本和表達式訪問;若是在腳本中聲明一個bean,那麼該實例只能用於腳本表達式中。
JSP文件提供了幾個可在腳本和表達式中使用的隱式變量。之因此稱它們爲隱式變量:是由於不須要在任何位置定義或聲明便可使用它們。它們被定義在JSP執行的Servlet方法的開頭。
import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.jsp.*; public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase implements org.apache.jasper.runtime.JspSourceDependent, org.apache.jasper.runtime.JspSourceImports { //... public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response) throws java.io.IOException, javax.servlet.ServletException { //... //隱式變量(pageContext、session、application、config、out、page)的聲明。 //另外,_jspService方法的參數request和response也是隱式變量 final javax.servlet.jsp.PageContext pageContext; javax.servlet.http.HttpSession session = null; final javax.servlet.ServletContext application; final javax.servlet.ServletConfig config; javax.servlet.jsp.JspWriter out = null; final java.lang.Object page = this; javax.servlet.jsp.JspWriter _jspx_out = null; javax.servlet.jsp.PageContext _jspx_page_context = null; try { //隱式變量的賦值 response.setContentType("text/html;charset=UTF-8"); pageContext = _jspxFactory.getPageContext(this, request, response, null, true, 8192, true); _jspx_page_context = pageContext; application = pageContext.getServletContext(); config = pageContext.getServletConfig(); session = pageContext.getSession(); out = pageContext.getOut(); _jspx_out = out; //... } //... } }
若是出於某個緣由,須要直接向響應中輸出內容,那麼應該使用out變量。不過,在大多數狀況下,均可以直接使用表達式,或者在JSP中編寫文本或HTML內容。
pageContext提供了獲取請求特性和會話特性值、訪問請求和響應、包含其餘文件、轉發請求的幾個便利方法。
由於page指令的isErrorPage特性的默認值爲假,因此exception變量沒有在上面出現。若是建立了一個isErrorPage被設置爲真的JSP,那麼頁面中將會自動定義一個隱式變量exception,類型爲Throwable。
示例源碼連接:https://pan.baidu.com/s/17tSRQEQg1nlyVnjexbYfrA 密碼:tsqn
JavaServer Pages是一門用於開發表示層(也稱爲視圖)的技術。
儘管能夠在表示層中混合數據庫訪問操做或數學計算,但並非一個好主意。函數型語言、腳本語言和其餘從文件頭執行到尾的語言,例如PHP,固然能夠這麼作。可是不可能在選擇Java做爲平臺語言的時候,還繼續以這種方式進行開發。
在一個具備良好結構、乾淨代碼的應用程序中,表示層一般會與業務層分隔開,一樣也與數據持久層分隔開。實際上,在JSP中顯示動態內容,能夠不使用一行Java代碼。這使得應用開發者能夠專一於業務和數據邏輯,而用戶界面開發者則負責JSP的開發。
使用請求派發器
private void showTicketForm(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getRequestDispatcher("/WEB-INF/jsp/view/ticketForm.jsp") .forward(request, response); }
請求轉發以後,瀏覽器的URL不會改變。
示例源碼連接:https://pan.baidu.com/s/1x6nSWQWV6bKdRn6nkshWag 密碼:xx6t
參考:《Java Web高級編程》第4章