眾所周知,每一個JSP頁面都會被Web容器編譯成一個Java類,供web容器調用,並且產生HTML葉面回饋給使用者。而瞭解其中的變異方法和規則,對我們學習JSP是非常有好處的,可以說學習好了這個編譯原理,就已經學習好了大部分的JSP知識,剩下的工作就只剩下熟記一些tablib和反覆應用以使自己更加熟練而已了。。
先來看一下JSP頁面所對應的Class的基本結構。每一個JSP頁面都會被編譯成成如下的格式樣子,先給一個大致的印象,詳細的說明在後面。
public class My$jsp extends HttpJspBase {
static {}
public date$jsp() {}
private static boolean _jspx_inited = false;
public final void _jspx_init()
throws org.apache.jasper.runtime.JspException {};
public void _JSP pageservice(HttpServletRequest request,
HttpServletResponse response)
throws java.io.IOException, ServletException {
JspFactory _jspxFactory = null;
PageContext pageContext = null;
HttpSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
String _value = null;
try {
if (_jspx_inited == false) {
synchronized (this) {
if (_jspx_inited == false) {
_jspx_init();
_jspx_inited = true;
}
}
}
_jspxFactory = JspFactory.getDefaultFactory();
response.setContentType("text/html;charset=ISO-8859-1");
pageContext = _jspxFactory.getPageContext(this, request, response,
"", true, 8192, true);
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
// HTML // begin
out.write("<html>/r/n<head>/r/n<title>Date</title>/r/n" +
"</head>/r/n<body>/r/n<h3>/r/n" +
"The date is/r/n");
// end
// begin
out.println((new java.util.Date()).toString());
// end
// HTML // begin
out.write("/r/n </h3>/r/n </body>/r/n</html>");
// end
} catch (Throwable t) {
if (out != null && out.getBufferSize() != 0) {
out.clearBuffer();
}
if (pageContext != null) {
pageContext.handlePageException(t);
}
} finally {
if (_jspxFactory != null) {
_jspxFactory.releasePageContext(pageContext);
}
}
}
}
我們可以清楚地看到,這裡面最重要的函數就是pageservice,web容器在編譯好一個JSP類以後,就申請這個類的對象,並且直接調用pageservice來獲得Response,最後返回給客戶。作為細節,我們可以總結如下:
- 所有的JSP頁面翻譯出來的class,都從HttpJspBase繼承,並且命名為PageName$jsp
- 在第一次調用pageservice函數的時候,該class會進行一次初始化,而這個初始化函數是_jspx_init,如果我們想,我們還可以自訂這個函數,來實現JSP頁面的初始化。
- <% %> 這樣的代碼被轉換成什麼了?
這樣的代碼被直接轉成Java代碼放到pageservice函數裡面。
- <%! %> 這樣的代碼被轉換成什麼了?
這樣的代碼被翻譯成成員函數和成員變數,也就是說,這些聲明在JSP的生命週期內都是存在的。
- HTML代碼呢?
html代碼直接被寫到PrintWriter裡面回饋給使用者。非常的直接
- 為什麼JSP頁面有那麼多省寫方式,比如說session , out , page , context之類。
這都是在pageservice裡面定義的臨時變數,具體的初始化可以參看上面的例子代碼,每一次調用JSP頁面,這些變數都會被重新初始化一次。當然我們也可以方便的聲明自己的變數。
- 省寫方式<%= object.doSomething()%> 這麼理解? 這種省寫方式調用doSomething所得到的Object的toString(),然後直接寫到out裡面。相當於:
out.print(object.doSomethiing().toString())
- JavaBean 裡面的scope定義了範圍範圍,這個範圍在這裡的意思是?
這是Bean物件控點儲存的地方的意思。我們可以想象一下,一個page範圍的Bean只是pageservice裡面的一個局部變數,當一次處理結束後,這個變數就會被Java虛擬機器回收。而session變數。而request層級的Bean就應該是JSP頁面的成員變數。而session和application則不能在JSP頁面class裡面儲存,而應該儲存在JSP頁面的調用對象裡面。
- 關於<%@ page %>命令,這個就太簡單了,只是一個一個的對應為response.setContentType()的語句而已。
- 關於JSP頁面轉向問題。這個語句被翻譯為getServletContext().getRequestDispatcher("/List.jsp").forward(req, res);語句。
- <%@ include file="included.jsp" %> 遇到這個語句,JSP翻譯器就會把這個檔案的代碼和現在檔案的代碼混合然後一起編譯,產生JSP類。這個方法很好,可以讓我們統一文檔的樣式,比如說吧header寫成一個檔案,,而把footer也寫成一個JSP ,並且在index.html裡面把這兩個檔案包含近來,這樣,不管Content怎麼變,上下樣式都不會變,有利於樣式的統一。
以上是JSP翻譯過程的簡單探討,更加詳細的細節可以參考tomcat的原始碼,瞭解這些原理對於學習JSP來說,是非常重要的,能大大的提高學習的效率。