在JSP出現之前,為了實現動態網頁的效果,服務器端利用 Servlet 的輸出流向客戶端發送HTML標簽以及HTML頁面中的內容,但是在多數動態網頁中,絕大部分內容是靜態的,只有少量內容需要動態實現。但是為了這少量的動態內容,程序猿依然要用Servlet 輸出其中所有的靜態內容,這就使得整個Servlet 程序代碼非常臃腫,導致Servlet 的開發效率非常低下。
為了彌補Servlet 的缺陷,SUN公司在Servlet 的基礎上推出了JSP(Java Server Pages)技術作為解決方案。JSP是簡化Servlet 編寫的一種技術,它由態部分和動態部分兩部分組成,靜態部分用於寫入標准的HTML標簽及內容;動態部分就是嵌入的Java代碼與JSP動態標簽了。通過這種方式,使靜態的部分直接使用HTML代碼編寫,對於動態的內容則使用 JAVA 腳本編寫。
對於Servlet 來說,無論動態、靜態都用Java代碼編寫;而JSP則將靜態的分出來,全部用HTML寫(底層還是使用Java包裝);動態的用Java 寫。究其本質還是一樣的,所以說,JSP的本質就是一種特殊的Servlet 。
JSP = HTML + Java 腳本 + JSP 標簽(指令),JSP中三種Java 腳本:
● <%...%>:Java代碼片段,用於定義0~N條Java 語句,方法中能夠寫什麼,這裡面就能放什麼;
● <%= %>:Java 表達式,用於輸出一條表達式或變量的結果。 response.getWriter().print() 方法中能夠寫什麼,這裡面就能夠寫什麼;
● <%! … %> :聲明,用來創建類的成員變量和成員方法,Java 類中能夠寫什麼,這裡面就能夠寫什麼,要注意的是,裡面的內容不在 _jspService() 方法之內,直接被JSP轉化後的類體包含。
前面已經闡述過,JSP的本質實質是一種特殊形式的 Servlet :
● 當用戶訪問一個JSP頁面時,會向 Servlet 容器(這裡是Tomcat)發出請求;
● 如果這個JSP頁面是第一次被訪問或者這個頁面被改動過時,服務器會把JSP 編譯成 .java文件,當然,這個.java 就是一個servlet類,然後再把 .java 文件編譯成.class 文件。因為編譯會耗費一定時間,所以頁面在第一次被訪問或改動後被訪問時會花費較長的訪問時間;
● 創建該類對象,最後由Servlet 容器調用它的service() 方法;
● 第二次請求同一JSP時,直接調用service() 方法。
首先,我們來寫一個hello.jsp文件:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <% String s = request.getHeader("User-Agent"); %>然後讓我們在tomcat 下找到被編譯成的.java 文件,為了節省空間,我把一些解釋標記在代碼注釋中:My JSP 'a.jsp' starting page 姓名 年齡 性別 <% for(int i = 0; i < 10; i++) { %> 張三 18 男 <% }%> <%! public void fun1() { System.out.println("hello"); } %> <%int a = 10; %> <%a++; %> <%=a %>
/* * Generated by the Jasper component of Apache Tomcat * Version: Apache Tomcat/7.0.42 * Generated at: 2015-12-27 10:03:19 UTC * Note: The last modified time of this file was set to * the last modified time of the source file after * generation to assist with modification tracking. */ package org.apache.jsp; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.jsp.*; import java.util.*; public final class hello_jsp extends org.apache.jasper.runtime.HttpJspBase implements org.apache.jasper.runtime.JspSourceDependent { public void fun1() { //要特別注意,這是在<%! %>中定義的方法,沒有被放在service方法中 System.out.println("hello"); } private static final javax.servlet.jsp.JspFactory _jspxFactory = javax.servlet.jsp.JspFactory.getDefaultFactory(); private static java.util.Map得出結論,JSP中雖然能直接寫出HTML代碼,但是在底層依然是被包裝成 Servlet 實現方式的,所以更印證了JSP是特殊的Servlet 。_jspx_dependants; private javax.el.ExpressionFactory _el_expressionfactory; private org.apache.tomcat.InstanceManager _jsp_instancemanager; public java.util.Map getDependants() { return _jspx_dependants; } public void _jspInit() { _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory(); _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig()); } public void _jspDestroy() { } public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response) throws java.io.IOException, javax.servlet.ServletException { //這是_jspService()方法 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.write("\r\n"); out.write("\r\n"); out.write("\r\n"); String s = request.getHeader("User-Agent"); //它的原身是String s = request.getHeader("User-Agent");,被直接拿過來了 out.write("\r\n"); out.write("\r\n"); out.write("\r\n"); out.write("\r\n"); //這些都是HTML代碼,底層被包裝成和Servlet 一樣的實現方式 out.write(" \r\n"); out.write(" My JSP 'a.jsp' starting page \r\n"); out.write(" \r\n"); out.write("\t\r\n"); out.write("\t\r\n"); out.write("\t \r\n"); out.write("\t\r\n"); out.write("\t\r\n"); out.write("\t\r\n"); out.write("\r\n"); out.write(" \r\n"); out.write(" \r\n"); out.write(" \r\n"); out.write(" \r\n"); out.write("\t\r\n"); out.write("\t姓名 \r\n"); out.write("\t年齡 \r\n"); out.write("\t性別 \r\n"); out.write("\t\r\n"); for(int i = 0; i < 10; i++) { //這也是直接拿過來的 out.write("\t\r\n"); out.write("\t\r\n"); out.write("\t 張三\r\n"); out.write("\t 18\r\n"); out.write("\t 男\r\n"); out.write("\t\r\n"); } out.write("\r\n"); out.write("\r\n"); out.write("\r\n"); out.write("\r\n"); out.write("\r\n"); out.write('\r'); out.write('\n'); int a = 10; //這些是定義的變量,可以看到是放在了service方法中的 out.write("\r\n"); out.write("\r\n"); a++; out.write("\r\n"); out.write("\r\n"); out.print(a ); out.write("\r\n"); out.write("\r\n"); out.write(" \r\n"); out.write("\r\n"); } catch (java.lang.Throwable t) { if (!(t instanceof javax.servlet.jsp.SkipPageException)){ out = _jspx_out; if (out != null && out.getBufferSize() != 0) try { out.clearBuffer(); } catch (java.io.IOException e) {} if (_jspx_page_context != null) _jspx_page_context.handlePageException(t); else throw new ServletException(t); } } finally { _jspxFactory.releasePageContext(_jspx_page_context); } } }
其次,在<% %> 和 <%= %> 腳本中定義的Java 代碼都會放在JSP 的 _jspService() 方法中(實際上就是Servlet中的service 方法),而<%! %> 腳本中定義的卻會放到 hello_jsp 類的成員位置的,這一點很重要,因為JSP中鼎鼎大名的九大內置對象是在_jspService() 方法中初始化的,只有在本方法中才能夠使用內置對象,所以<%! %> 腳本中是不能使用內置對象的(在實際開發中,本腳本很少用到)。
缺點:不適合設置html響應體,需要大量的response.getWriter().print("")
優點:動態資源,可以編程。
缺點:html是靜態頁面,不能包含動態信息
優點:不用為輸出html標簽而發愁
優點:在原有html的基礎上添加java腳本,構成jsp頁面。
在設計中,JSP和Servlet 是相互配合使用的,其分工為:
作為請求發起頁面,例如顯示表單、超鏈接,並將請求發給 Servlet ;
作為請求結束頁面,例如顯示數據。
作為請求中處理數據的環節。