本系列文章旨在記錄和總結自己在Java Web開發之路上的知識點、經驗、問題和思考,希望能幫助更多(Java)碼農和想成為(Java)碼農的人。
目錄
- 介紹
- Servlet要生成HTML
- JSP核心原理
- JSP基本文法
- 這樣更好了解
- 總結
介紹
前面的文章使用Servlet技術實作了一個簡單的租房網平台。
但是,它有很多不足,最直覺的感受應該就是:
- 在Java代碼裡輸出HTML代碼實在是太難看、太繁瑣、太容易出錯了;
- 如果想改變一下HTML呈現效果就必須修改HTML代碼,是以還必須重新編譯Java代碼。
是以,JSP技術應運而生了。
JSP的全稱是JavaServer Page,截止到目前最新版本好像是2.3,但在JCP官網(https://jcp.org/en/home/index)又隻能搜到2.1版本(關于JCP上如何下載下傳規範可以參考這篇文章)。如果有讀者知道如何下載下傳JSP 2.3版本的規範(或者沒有),請告知一下。
Servlet要生成HTML
對于Web應用來說,HTML頁面往往很重要,數量也比較多,每個頁面中的HTML代碼也比較多。
而動态生成的HTML頁面也是如此,大部分展示結構/效果/元素都是相同的,僅僅隻是資料不一樣。

可以這麼說,Servlet代碼中大部分是HTML代碼。
是以,我們是否能夠運用逆向思維:在HTML頁面中通過某種文法嵌入少量的Java代碼,然後讓工具自動生成Servlet的代碼?
JSP核心原理
答案當然是可行的,JSP正是這樣的技術。
這種在HTML頁面中嵌入Java代碼的頁面就叫JSP頁面。我們編寫JSP頁面,由Servlet容器轉換成Servlet并編譯成Java位元組碼檔案,當第一個該JSP頁面的請求到來時Servlet容器就會加載對應的Servlet并生成Servlet對象,後面的處理就跟前面所介紹的Servlet技術相同了。
是以,第一次請求JSP頁面的時候,總會比較慢,因為需要轉換成Servlet并編譯,再加載Servlet類檔案并生成Servlet執行個體。
當然,我們(就是Servlet容器的開發者)就必須改造Servlet容器,讓它能夠識别、轉換JSP頁面,然後能夠自動的編譯轉換成的Servlet代碼。這樣的Servlet容器就叫做JSP容器,是以合起來叫做Servlet/JSP容器。
Servlet/JSP容器甚至能夠自動檢測到JSP頁面的修改并重新編譯和加載,是以,我們修改JSP頁面後并不需要重新啟動Servlet/JSP容器。
可以說,JSP的核心原理/本質就是Servlet。
JSP基本文法
既然是HTML代碼中嵌入Java代碼,那麼JSP頁面的主體當然就是HTML,這部分就使用HTML的文法編寫。
那麼Servlet/JSP容器是如何識别嵌入的Java代碼呢?
JSP使用符号“”和“%>”來将Java代碼或與Java代碼相關的東西包圍住,比如:
- “”和“--%>”及其中間的内容是JSP的注釋,它不會發送到浏覽器端,差別于HTML的注釋“”。
- “”和“%>”及其中間的的内容是JSP的指令,用來設定頁面的一些參數,import依賴的Java包,包含其他JSP頁面等等。
- “”和“%>”中間可以直接寫Java代碼,這叫JSP的腳本。
- “”和“%>”及其中間的的内容是JSP的表達式,用來直接輸出表達式的計算結果。
- “”和“%>”及其中間的的内容是JSP的聲明,用來聲明該頁面使用的變量和方法。
是以,Servlet/JSP容器一旦遇到上述的符号,它就知道該如何轉換成Java代碼了。
JSP其他技術
事實上,JSP技術在不斷的演化(任何技術都是這樣,演化就是為了進步,發現不足,解決不足)。
JSP其他的相關技術有:
- JSP的動作,它很像HTML或XML的标簽,我猜測它就是使用下面的JSP的标簽技術實作的,比如:
- JSP的标簽,這包括JSP自帶的标準标簽JSTL(JSP Standard Tag Library,即JSP标準标簽庫)、可以擴充自己所需要标簽的自定義标簽技術和标簽檔案技術。它的使用很像XML标簽,當然,擴充自己的标簽就比較複雜了。
- 表達式語言(Expression Language,簡稱EL),似乎此技術與JSP技術是互相獨立的,但JSP裡面用它還是相當頻繁的,不知道當時開發EL是否主要就是為了在JSP中使用,我沒有去深究了。
- JSP頁面片段,主要是為了消除重複,提高複用。
可以明确的是,目前JSP的主要用途就是起到一個模闆的作用,并盡量采用EL來通路應用資料,而不再使用JSP聲明、JSP表達式和JSP腳本來實作業務邏輯,業務邏輯交給獨立的Servlet來實作(當然,後來出現的MVC模式中Servlet僅用來充當控制器)。
這樣更好了解
我們把JSP頁面的内容分成兩大類:
- 一類就是HTML代碼,這個就像編寫普通的HTML頁面那樣編寫就行,我們把這類代碼叫做模闆元素。
- 另一類就是JSP相關的部分,這是通過規定的某種符号來識别的,這類代碼就叫做JSP文法元素。
無論如何,一個JSP頁面都會轉換成一個Servlet,即不管是模闆元素還是JSP文法元素,它最終都會轉換成Java代碼。
是以,轉換成的這些Java代碼必然在一個Servlet類的源代碼中,而Servlet接口中最重要的方法就是那個service()方法。
這樣看來,轉換成的這些Java代碼可以看作是service()方法中的内容,比如:
- 模闆元素其實轉換成Java代碼就是:writer.println("")等類似這樣的。
- JSP腳本則直接拷貝即可。
- JSP隐式對象實際上是Servlet/JSP容器生成并傳進來的參數,比如request就是HttpServletRequest執行個體、application就是ServletContext執行個體、config就是ServletConfig執行個體、pageContext就是javax.servlet.jsp.PageContext執行個體、out是javax.servlet.jsp.JspWriter執行個體(類似從ServletResponse.getWriter()獲得的Writer)等等。
- JSP表達式就轉換成:writer.print("表達式的計算結果")。
- JSP動作和标簽肯定就轉換成對應的處理類的調用代碼。
- EL則轉成通路附加到某個隐式對象的屬性中的資料,即getAttribute("xxx")名為xxx的屬性對象(它其實是你的業務實體類對象),然後就可以通路xxx中的資料。
還有一些則轉換成Servlet類檔案的其他内容,比如:
- JSP的page指令導入Java包的部分。
- JSP聲明可以聲明該頁面使用的變量和方法,即該頁面轉換成的Servlet類的成員變量和成員方法。
總結
現在,我們就可以使用JSP技術來改造前面的租房網應用了。
- 我們要善于使用逆向思維;
- 一旦你覺得寫代碼很繁瑣,很多重複的地方,那麼就表示哪裡有問題,或者應該改進,甚至有可能抽象出來一種新技術;
- JSP的本質就是Servlet,因為它需要被轉換成Servlet;
- JSP目前一般用來當HTML模闆,僅使用EL來通路資料,而不在其中編寫Java腳本了。