
在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");
%>
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 %>
然後讓我們在tomcat 下找到被編譯成的.java 文件,為了節省空間,我把一些解釋標記在代碼注釋中:
/*
* 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 _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);
}
}
}
得出結論,JSP中雖然能直接寫出HTML代碼,但是在底層依然是被包裝成 Servlet 實現方式的,所以更印證了JSP是特殊的Servlet 。
其次,在<% %> 和 <%= %> 腳本中定義的Java 代碼都會放在JSP 的 _jspService() 方法中(實際上就是Servlet中的service 方法),而<%! %> 腳本中定義的卻會放到 hello_jsp 類的成員位置的,這一點很重要,因為JSP中鼎鼎大名的九大內置對象是在_jspService() 方法中初始化的,只有在本方法中才能夠使用內置對象,所以<%! %> 腳本中是不能使用內置對象的(在實際開發中,本腳本很少用到)。
缺點:不適合設置html響應體,需要大量的response.getWriter().print("")
優點:動態資源,可以編程。
缺點:html是靜態頁面,不能包含動態信息
優點:不用為輸出html標簽而發愁
優點:在原有html的基礎上添加java腳本,構成jsp頁面。
在設計中,JSP和Servlet 是相互配合使用的,其分工為:
作為請求發起頁面,例如顯示表單、超鏈接,並將請求發給 Servlet ;
作為請求結束頁面,例如顯示數據。
作為請求中處理數據的環節。
