JSP/Servlet基礎語法

Andrew.Hann發表於2014-05-15

相關學習資料

http://my.oschina.net/chape/blog/170247
http://docs.oracle.com/cd/E13222_01/wls/docs81/webapp/web_xml.html
http://blog.csdn.net/liaoxiaohua1981/article/details/6761053
http://computer.c.blog.163.com/blog/static/102524482012314537670/
http://www.blogjava.net/w2gavin/articles/358641.html
http://www.douban.com/note/310522851/
http://mianhuaman.iteye.com/blog/1105522
http://blog.csdn.net/li_jinjian2005/article/details/2915462
http://210.44.193.6/JSP/07.htm
http://www.ibm.com/developerworks/cn/java/j-lo-servlet30/

 

目錄

1. J2EE WEB應用檔案目錄結構
2. web.xml基礎語法
3. JSP基礎語法
4. Servlet基礎語法

 

1. J2EE WEB應用檔案目錄結構

Java Web應用由一組靜態HTML頁、Servlet、JSP和其他相關的class組成,它們一起構成一個大的工程專案。每種元件在Web應用中都有固定的存放目錄。Web應用的配置資訊存放在web.xml檔案中。在釋出某些元件(如Servlet)時,必須在web.xml檔案中新增相應的配置資訊
Java Web應用程式必須使用規範的目錄結構

1. 應用程式根目錄,可以取任意的名字,所有的HTML、JSP檔案都放在這個目錄下
    1.1 WEB-INF目錄: 必須目錄 
        1.1.1 web.xml: Web應用部署描述檔案,必須檔案
        1.1.2 classes目錄: 
            1) 用於存放單個*.classes位元組碼檔案,Servlet類檔案也存放在這個目錄下
        1.1.3 lib目錄: 
            1) 存放第三方類庫檔案,即打包後的JAR檔案
        1.1.4 TLD檔案: 標籤庫描述檔案 
    1.2 其他靜態檔案:
        1.2.1 HTML
        1.2.2 CSS
        1.2.3 JavaScript
        1.2.4 圖片等
    1.3 *.jsp: 存放任意多個JSP頁面

 

2. web.xml基礎語法

位於每個WEB應用的的WEB-INF路徑下的web.xml檔案被稱為配置描述符,這個web.xml檔案對於Java Web應用十分重要,總體來說,web.xml主要負責以下內容:

1. JSP環境引數初始化
2. 配置和管理Servlet
3. 配置和管理Listener
4. 配置和管理Filter
5. 配置和管理JNDI
6. Session配置
7. MIME TYPE配置
8. 錯誤處理
9. 配置標籤庫
10. 配置JSP屬性
11. 配置和管理JAAS授權認證
12. 配置和管理資源引用
13. WEB應用預設首頁(welcome檔案)的設定

下面我儘量列出了一個完整的web.xml的結構,我使用了/**/註釋符來說明某個專案的說明,要明白的是,在真實的web.xml中不允許使用/**/註釋符的,只是我覺得直接在web.xml中插入解釋說明能更好的說明問題

/*
<?xml version="1.0" encoding="GBK"?>是一個基本的XML檔案的框架,不管是什麼配置檔案,只要是基於XML的,它的基本結構都是這樣
*/
<?xml version="1.0" encoding="GBK"?>
/*
web.xml檔案的根元素是<web-app.../>元素,整個web.xml只有這個根元素,每個web.xml必須以這個<web-app>根元素作為開頭,在Servlet 3.0規範中,該元素新增了
metadata-complete屬性,當該屬性值為true時,該web應用"不會"載入Annotation配置的WEB元件(如Servlet、Filter、Listener等),反之則載入
*/ <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> /* 1. icon資訊: 用來指定web站點中小圖示和大圖示的路徑 1) small-icon: 大小為16 X 16 pixel,但是圖象檔案必須為GIF或JPEG格式,副檔名必須為:.gif或 .jpg. 2) large-icon: 大小為32 X 32 pixel,但是圖象檔案必須為GIF或JPEG的格式,副檔名必須為; gif 或jpg. */ <small-icon>/路徑/smallicon.gif</small-icon> <large-icon>/路徑/largeicon-jpg</large-icon> /* 2. 描述資訊 display-name: 定義站點的名稱 description: 對站點的描述 */ <display-name>站點名稱</display-name> <description>站點描述</discription> /* 3. distributable distributable元素為空標籤,它的存在與否可以指定站臺是否可分散式處理.如果web.xml中出現這個元素,則代表站臺在開發時已經被設計為能在多個JSP Container之間分散執行 */ <distributable/> /* 4. JSP環境引數: context-param context-param元素用來設定web站臺的環境引數(context),它包含兩個子元素: 1) param-name: 引數名稱 2) param-value: 值 此所設定的引數,在JSP網頁中可以使用下列方法來取得: ${initParam.param_name} 若在Servlet可以使用下列方法來獲得: String param_name=getServletContext().getInitParamter("param_name"); */ <context-param> <param-name>param_name</param-name> <param-value>param_value</param-value> </context-param> /* 5. filter過濾器、filter-mapping 用於指定WEB容器的過濾器,在請求和響應物件在Servlet處理之前和之後,可以通過此過濾器對這兩個物件進行處理 filter-class 中指定的過濾器類須繼承 javax.servlet.Filter具有須有以下三種方法 init(FilterConfig filterConfig):初始化;一般情況下時讀取配置檔案中的init-param引數值 如 filterConfig.getInitParameter("encoding") doFilter(...):用於對request,response進行處理,並能過chain.doFilter(...) 交過下一個控制器 destroy():資源銷燬 filter-mapping則指示需要進行過濾器處理的URL訪問模式,可以理解為當我們的URL匹配到指定的模式後,則對這個請求執行指定的"過濾處理流程"(可以把它理解為一種路由機制) */ <filter> <small-icon>/路徑/smallicon.gif</small-icon> <large-icon>/路徑/largeicon-jpg</large-icon> <filter-name>encodingfilter</filter-name> <display-name>站點名稱</display-name> <description>站點描述</discription> <filter-class>com.my.app.EncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingfilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> /* 6. servlet、servlet-mapping 和filter過濾器類似,servlet也是用來配置對映處理機制的 和filter-mapping的作用類似,servlet-mapping用來定義servlet所對應URL. */ <servlet> <small-icon>/路徑/smallicon.gif</small-icon> <large-icon>/路徑/largeicon-jpg</large-icon> <servlet-name>MyServletName</servlet-name> <display-name>站點名稱</display-name> <description>站點描述</discription> /* servlet-class、jsp-file有且只能出現一個 */ <servlet-class>com.Little.MyServlet</servlet-class> <jsp-file>/path/index.jsp</jsp-file> <init-param> <param-name>name1</param-name> <param-value>value1</param-value> </init-param> /* 指定當Web應用啟動時,裝載Servlet的次序 1) 當值為正數或零時: 容器在應用啟動時就載入這個servlet,Servlet容器先載入數值小的servlet,再依次載入其他數值大的servlet 2) 當值為負或未定義: 容器在該servlet被選擇時才載入,即Servlet容器將在Web客戶首次訪問這個servlet時載入它 */ <load-on-startup></load-on-startup> /* 設定執行時角色,可以使當前Servlet以一個特定的角色執行,有利於安全許可權控制 */ <run-as> <description>Security role for anonymous access</description> <role-name>tomcat</role-name> </run-as> /* security-role-ref子元素提供出現在伺服器專用口令檔案中的安全形色名的一個別名。例如,假如編寫了一個呼叫 request.isUserInRole("boss")的servlet,
但後來該servlet被用在了一個其口令檔案呼叫角色manager而不 是boss的伺服器中。下面的程式段使該servlet能夠使用這兩個名稱中的任何一個
*/ <security-role-ref> <role-name>boss</role-name> <!-- New alias --> <role-link>manager</role-link> <!-- Real name --> </security-role-ref> </servlet> <servlet-mapping> <servlet-name>LoginChecker</servlet-name> <url-pattern>/LoginChecker</url-pattern> <servlet-name>MyServletName</<servlet-name> </servlet-mapping> /* 7. security-role(虛擬安全使用者) 給出安全形色的一個列表,這些角色將出現在servlet元素內的security-role-ref元素的role-name元素中。分別宣告角色可使高階IDE處理安全資訊更為容易。 */ <security-role> <description>安全賬戶描述</discription> <role-name>admin</role-name> </security-role> /* 8. listener 監聽器也叫Listener,是Servlet的監聽器,它可以監聽客戶端的請求、服務端的操作等。通過監聽器,可以自動激發一些操作,Servlet本身在一些特定的關鍵處理流程節點上增加Hook
回撥機制,使得我們可以在這些節點位置配置監聽器 常見的監聽器如下: Listener介面 1) ServletContextListener: ServletContextEvent 2) ServletContextAttributeListener: ServletContextAttributeEvent 3) HttpSessionListener: HttpSessionEvent 4) HttpSessionActivationListener: HttpSessionAttributeListener 5) HttpSessionBindingEvent: HttpSessionBindingListener 6) ServletRequestListener: ServletRequestEvent 7) ServletRequestAttributeListener: ServletRequestAttributeEvent
*/ <listener> <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> </listener> /* 9. session配置 session-config包含一個子元素session-timeout.定義web站臺中的session引數,定義這個web站臺所有session的有效期限.單位為分鐘 */ <session-config> <session-timeout>20</session-timeout> </session-config> /* 10. mime-mapping mime-mapping包含兩個子元素extension和mime-type.定義某一個副檔名和某一MIME Type做對映,和apache中的檔案擴充套件處理器原理類似,對指定的副檔名指定相應的處理程式 */ <mime-mapping> <extension>doc</extension> <mime-type>application/vnd.ms-word</mime-type> </mime-mapping> <mime-mapping> <extension>xls</extension> <mime-type>application/vnd.ms-excel</mime-type> </mime-mapping> /* 11. welcome-file-list welcome-file-list包含一個子元素welcome-file.用來定義首頁列單,即當客戶端的請求沒有指定具體的頁面時,服務區器預設指定的首頁尾本 */ <welcome-file-list> <welcome-file>index.jsp</welcome-file> <welcome-file>index.htm</welcome-file> </welcome-file-list> /* 12. error-page 錯誤處理機制,error-page元素包含三個子元素error-code,exception-type和location.將錯誤程式碼(Error Code)或異常(Exception)的種類對應到web站點資源路徑。
簡單來說就是返回特定HTTP狀態程式碼或者特定型別的異常被丟擲時,制定響應將要顯示的頁面。
*/ <error-page> <error-code>404</error-code> <exception-type>java.lang.Exception</exception-type> <location>/error404.jsp</location> </error-page> <error-page> <exception-type>java.lang.Exception</exception-type> <exception-type>java.lang.NullException</exception-type> <location>/except.jsp</location> </error-page> /* 13. jsp-config JSP相關配置 */ <jsp-config> <taglib> /* taglib-uri定義TLD檔案的URI,JSP網頁的taglib指令可以經由這個URI存取到TLD檔案 */ <taglib-uri>Taglib</taglib-uri> /* taglib-location定義TLD檔案對應Web站臺的存放位置 */ <taglib-location>/WEB-INF/tlds/MyTaglib.tld</taglib-location> </taglib> <jsp-property-group> <description> Special property group for JSP Configuration JSP example. </description> <display-name>JSPConfiguration</display-name> /* 設定值所影響的範圍,如:/CH2 或者/*.jsp */ <uri-pattern>/*</uri-pattern> /* 若為true,表示不支援EL語法 */ <el-ignored>true</el-ignored> /* 設定JSP網頁的編碼 */ <page-encoding>GB2312</page-encoding> /* 若為true表示不支援<%scription%>語法. */ <scripting-inivalid>true</scripting-inivalid> /* 設定JSP網頁的結尾,副檔名為.jspf */ <include-coda>.jspf</include-coda> /* 設定JSP網頁的抬頭,副檔名為.jspf */ <include-prelude>.jspf</include-prelude> </jsp-property-group> </jsp-config> /* 14. resource-ref、resource-env-ref resource-ref宣告資源工廠使用的外部資源 resource-env-ref宣告與資源相關的管理物件 */ <resource-ref> <description>JNDI JDBC DataSource of JSPBook</description> <!-- 資源說明 --> <res-ref-name>jdbc/sample_db</res-ref-name> <!-- 資源名稱 --> <res-type>javax.sql.DataSoruce</res-type> <!-- 資源種類 --> <res-auth>Container</res-auth> <!-- 資源由Application或Container來許可 --> <res-sharing-scope>Shareable|Unshareable</res-sharing-scope> <!-- 資源是否可以共享.預設值為 Shareable --> </resource-ref> <resource-env-ref> <resource-env-ref-name>jms/StockQueue</resource-env-ref-name> </resource-env-ref> /* 15. EJB配置 ejb-ref用於宣告一個EJB的主目錄的引用 用於宣告一個EJB的本地主目錄的應用。 */ <ejb-ref> <description>Example EJB reference</decription> <ejb-ref-name>ejb/Account</ejb-ref-name> <ejb-ref-type>Entity</ejb-ref-type> <home>com.mycompany.mypackage.AccountHome</home> <remote>com.mycompany.mypackage.Account</remote> </ejb-ref> <ejb-local-ref> <description>Example Loacal EJB reference</decription> <ejb-ref-name>ejb/ProcessOrder</ejb-ref-name> <ejb-ref-type>Session</ejb-ref-type> <local-home>com.mycompany.mypackage.ProcessOrderHome</local-home> <local>com.mycompany.mypackage.ProcessOrder</local> </ejb-local-ref> /* 16. WEB應用環境引數配置 */ <env-entry> <description>環境引數說明</description> <env-entry-name>minExemptions</env-entry-name> <env-entry-value>1</env-entry-value> <env-entry-type>java.lang.Integer</env-entry-type> </env-entry> /* 17. 安全配置、資源限制訪問配置 在Web應用程式的web.xml中建立security-constraint、login-config和security-role元素 */ /* 配置對指定資源、指定角色的訪問許可權 */ <security-constraint> <web-resource-collection> <web-resource-name>HelloServlet</web-resource-name> <url-pattern>/HelloServlet</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <description>This applies only to the "tomcat" security role</description> <role-name>admin</role-name> </auth-constraint> <user-data-constraint> <transport-guarantee>NONE</transport-guarantee> </user-data-constraint> </security-constraint> /* auth-method的方法有: 1) BASIC BASIC是一種常見的Web認證方式,瀏覽器給使用者提示一個對話方塊,要求輸入使用者名稱和密碼,隨後Tomcat將給出的使用者名稱和密碼與tomcat-users.xml中的使用者名稱和密碼進行比較,
然後使用前面的security-constraint配置來確定使用者是否可訪問受保護的servlet 2) FORM 3) CLIENT-CERT 4) DIGEST
*/ <login-config> <realm-name>在HTTP驗證返回包中的顯示名稱</<realm-name> <auth-method>BASIC</auth-method> <form-login-config>如果auth-method採用FORM,則這裡填寫form-login-config名稱</form-login-config> </login-config> /* 關於security-role,在前面的servlet已經說明過,這裡要強調一下: web.xml中的HTTP認證方法實際上有兩個步驟: 1) 檢查提供的使用者名稱和密碼是否正確。 2) 判斷使用者是否對映到特定的安全形色。例如,使用者可能提供了正確的使用者名稱和密碼,但沒有對映到特定的安全形色,也將被禁止訪問特定的Web資源。 */ <security-role> <role-name>admin</role-name> </security-role> </web-app>

以上就是web.xml的完整結構,需要注意的是,web.xml中有一些環境引數的載入配置,它們之間存在優先順序的關係,我們在編寫配置的時候需要注意這一點

web.xml 的載入順序是:context-param -> listener -> filter -> servlet ,而相同型別節點之間的程式呼叫的順序是根據對應的mapping的順序進行呼叫的

 

3. JSP基礎語法

JSP的本質是Servlet,當使用者向指定Servlet傳送請求時,Servlet利用輸出流動態生成HTML頁面,包括每一個靜態的HTML標籤和所有在HTML頁面中出現的內容
JSP頁面由如下兩部分組成

1. 靜態部分: 標準的HTML標籤、靜態的頁面內容,也就是普通的HTML程式碼
2. 動態部分: 受java程式控制的內容,這些內容由Java程式來動態生成 

/*
1. JSP的編譯指令 
*/
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>learn j2ee</title> 
    /*
    2. JSP註釋 
    */ 
</head> 
/*
3. JSP宣告 
*/
<body> 
/*
4. JSP表示式
*/
/*
5. JSP指令碼
*/ 
/*
6. JSP的動作指令
*/ 
/*
7. JSP指令碼中的內建物件
*/
</body>
</html>

0x1: JSP的編譯指令

JSP的編譯指令是通過JSP引擎的訊息,它不直接生成輸出。編譯指令都有預設值,我們並不需要為每個指令設定值。它的格式如下
<%@ 編譯指令名 屬性名="屬性值"...%>(不同屬性名之間用空格分開)
    1) page: Page指令為容器提供當前頁面的使用說明。一個JSP頁面可以包含多個page指令
        1.1) buffer: 指定緩衝區的大小。緩衝區是JSP內部物件"out",它用於快取JSP頁面對客戶端瀏覽器的輸出,預設值為8KB,可以設定為none,也可以設定為其他的值,單位為Kb
        1.2) autoFlush: 當緩衝區即將滿而溢位時,是否需要強制輸出緩衝區的內容:
            1.2.1) 如果設定為true則正常輸出
            1.2.2) 如果設定為false,則會在buffer溢位時產生一個異常
        1.3) contentType: 用於設定生成網頁的檔案格式(MIME型別)、和編碼字符集(頁面字符集型別)(text/html;charSet=ISO-8859-1)
            1.3.1) 預設的MIME型別是text/html
            1.3.2) 預設的字符集型別為ISO-8859-1
        1.4) errorPage: 指定錯誤處理頁面,如果本頁面產生了異常或錯誤,而該JSP頁面沒有對應的錯誤處理程式碼(沒有用try、catch機制捕捉異常),則會自動呼叫該屬性所指定的JSP
頁面。值得注意的是,為頁面指定錯誤發生時的錯誤提示頁面是一種安全的做法,能夠在一定程度上組織error-based-sql-injection的攻擊 1.5) isErrorPage: 指定當前頁面是否可以作為另一個JSP頁面的錯誤處理頁面 1.6) extends: JSP程式編譯時所產生的Java類,需要繼承的父類,或者需要實現的介面的全限定類名(即包含包名在內的完整路徑) 1.7) import: 用來匯入包。預設自動匯入的包(引數之間用逗號分隔)(java.lang.*,javax.servlet.*) 1.7.1) java.lang.* 1.7.2) javax.servlet.* 1.7.3) javax.servlet.jsp.* 1.7.4) javax.servlet.http.* 1.8) info: 定義JSP頁面的描述資訊 1.9) isThreadSafe: 指定對JSP頁面的訪問是否為執行緒安全 1.10) language: 定義JSP頁面所用的指令碼語言,預設是Java 1.11) session: 指定JSP頁面是否使用session 1.12) isELIgnored: 指定是否執行EL表示式 1.13) isScriptingEnabled: 確定指令碼元素能否被使用 2) include: 用於指定包含另一個頁面 <%@include file="file.jsp"%> 可以將外部檔案嵌入到當前JSP檔案中,同時解析這個頁面中的JSP語句(如果有的話),也就是說,它既可以包含靜態的文字,也可以包含動態的JSP頁面。包含頁面在編譯時將完全包含了被包
含頁面的程式碼,融合成一個頁面。作用和PHP中的inlcude、require類似。 需要注意的是,要指出的是,靜態包含還會將被包含頁面的編譯指令也包含進來,如果兩個頁面的編譯指令衝突,那麼頁面就會出錯(即被包含的頁面中不能重複定義
page、include、taglib)
3) taglib: 用於定義和訪問自定義標籤 自定義標籤庫是一種非常優秀的表現層元件技術。通過使用自定義標籤庫,可以在簡單的標籤中封裝複雜的功能,在JSP2中使用自定義標籤需要以下步驟 1) 開發自定義標籤處理類 在JSP頁面使用一個標籤時,底層實際上由標籤處理類提供支援,從而可以通過簡單的標籤來封裝複雜的功能,從而使團隊更好地協作開發。自定義標籤類應該繼承一個父類:
javax.servlet.jsp.tagext.SimpleTagSupport,除此之外,JSP自定義標籤類還有如下要求(通過介面來強制性保證):
1) 如果標籤包含屬性,每個屬性都有對應的getter、setter方法 2) 重寫doTag()方法,這個方法負責生成頁面內容 example1: 無屬性、無標籤體的最簡單的標籤處理類 package lee; import javax.servlet.jsp.tagext.*; import javax.servlet.jsp.*; import java.io.*; public class HelloWorldTag extends SimpleTagSupport { //重寫doTag方法,該方法在標籤結束生成頁面內容 public void doTag()throws JspException, IOException { //獲取頁面輸出流,並輸出字串 getJspContext().getOut().write("Hello World " + new java.util.Date()); } } 這個標籤處理類繼承了SimpleTagSupport父類,並重寫了doTag()方法,doTag()負責輸出頁面內容(即標籤代表的內容) example2: 帶屬性的標籤處理類 package lee; import javax.servlet.jsp.tagext.*; import javax.servlet.jsp.*; import java.io.*; import java.sql.*; public class QueryTag extends SimpleTagSupport { //標籤的屬性 private String driver; private String url; private String user; private String pass; private String sql; //driver屬性的setter和getter方法 public void setDriver(String driver) { this.driver = driver; } public String getDriver() { return this.driver; } //url屬性的setter和getter方法 public void setUrl(String url) { this.url = url; } public String getUrl() { return this.url; } //user屬性的setter和getter方法 public void setUser(String user) { this.user = user; } public String getUser() { return this.user; } //pass屬性的setter和getter方法 public void setPass(String pass) { this.pass = pass; } public String getPass() { return this.pass; } //sql屬性的setter和getter方法 public void setSql(String sql) { this.sql = sql; } public String getSql() { return this.sql; } //conn屬性的setter和getter方法 public void setConn(Connection conn) { this.conn = conn; } public Connection getConn() { return this.conn; } //stmt屬性的setter和getter方法 public void setStmt(Statement stmt) { this.stmt = stmt; } public Statement getStmt() { return this.stmt; } //rs屬性的setter和getter方法 public void setRs(ResultSet rs) { this.rs = rs; } public ResultSet getRs() { return this.rs; } //rsmd屬性的setter和getter方法 public void setRsmd(ResultSetMetaData rsmd) { this.rsmd = rsmd; } public ResultSetMetaData getRsmd() { return this.rsmd; } //執行資料庫訪問的物件 private Connection conn = null; private Statement stmt = null; private ResultSet rs = null; private ResultSetMetaData rsmd = null; public void doTag()throws JspException, IOException { try { //註冊驅動 Class.forName(driver); //獲取資料庫連線 conn = DriverManager.getConnection(url,user,pass); //建立Statement物件 stmt = conn.createStatement(); //執行查詢 rs = stmt.executeQuery(sql); rsmd = rs.getMetaData(); //獲取列數目 int columnCount = rsmd.getColumnCount(); //獲取頁面輸出流 Writer out = getJspContext().getOut(); //在頁面輸出表格 out.write("<table border='1' bgColor='#9999cc' width='400'>"); //遍歷結果集 while (rs.next()) { out.write("<tr>"); //逐列輸出查詢到的資料 for (int i = 1 ; i <= columnCount ; i++ ) { out.write("<td>"); out.write(rs.getString(i)); out.write("</td>"); } out.write("</tr>"); } } catch(ClassNotFoundException cnfe) { cnfe.printStackTrace(); throw new JspException("自定義標籤錯誤" + cnfe.getMessage()); } catch (SQLException ex) { ex.printStackTrace(); throw new JspException("自定義標籤錯誤" + ex.getMessage()); } finally { //關閉結果集 try { if (rs != null) rs.close(); if (stmt != null) stmt.close(); if (conn != null) conn.close(); } catch (SQLException sqle) { sqle.printStackTrace(); } } } } example3: 帶標籤體的標籤處理類 帶標籤體的標籤可以在標籤內嵌入其他內容(包括靜態HTML、動態JSP內容),通常用於完成一些邏輯運算 package lee; import javax.servlet.jsp.tagext.*; import javax.servlet.jsp.*; import java.io.*; import java.sql.*; import java.util.*; public class IteratorTag extends SimpleTagSupport { //標籤屬性,用於指定需要被迭代的集合 private String collection; //標籤屬性,指定迭代集合元素,為集合元素指定的名稱 private String item; //collection屬性的setter和getter方法 public void setCollection(String collection) { this.collection = collection; } public String getCollection() { return this.collection; } //item屬性的setter和getter方法 public void setItem(String item) { this.item = item; } public String getItem() { return this.item; } //標籤的處理方法,簡單標籤處理類只需要重寫doTag方法 public void doTag() throws JspException, IOException { //從page scope中獲取屬性名為collection的集合 Collection itemList = (Collection)getJspContext().getAttribute(collection); //遍歷集合 for (Object s : itemList) { //將集合的元素設定到page 範圍 getJspContext().setAttribute(item, s); //輸出標籤體 getJspBody().invoke(null); } } } example4: 以"頁面片段"作為屬性的標籤處理類 package lee; import javax.servlet.jsp.tagext.*; import javax.servlet.jsp.*; import java.io.*; public class FragmentTag extends SimpleTagSupport { private JspFragment fragment; //fragment屬性的setter和getter方法 public void setFragment(JspFragment fragment) { this.fragment = fragment; } public JspFragment getFragment() { return this.fragment; } @Override public void doTag() throws JspException, IOException { JspWriter out = getJspContext().getOut(); out.println("<div style='padding:10px;border:1px solid black'>"); out.println("<h3>下面是動態傳入的JSP片段</h3>"); //呼叫、輸出“頁面片段” fragment.invoke( null ); out.println("</div"); } } 上面的程式定義了JspFragment型別的fragment屬性,該屬性代表了使用該標籤時的"頁面片段" example5: 動態屬性的標籤處理類 在某些特殊情況下,我們需要傳入自定義標籤的屬性個數是不確定的、屬性名也是不確定的,這就需要使用到動態屬性的標籤 package lee; import javax.servlet.jsp.tagext.*; import javax.servlet.jsp.*; import java.io.*; import java.util.*; public class DynaAttributesTag extends SimpleTagSupport implements DynamicAttributes { //儲存每個屬性名的集合 private ArrayList<String> keys = new ArrayList<String>(); //儲存每個屬性值的集合 private ArrayList<Object> values = new ArrayList<Object>(); @Override public void doTag() throws JspException, IOException { JspWriter out = getJspContext().getOut(); //此處只是簡單地輸出每個屬性 out.println("<ol>"); for( int i = 0; i < keys.size(); i++ ) { String key = keys.get( i ); Object value = values.get( i ); out.println( "<li>" + key + " = " + value + "</li>" ); } out.println("</ol>"); } /* 實現DynamicAttributes介面必須實現setDynamicAttribute,該方法用於為該標籤處理類動態新增屬性名、屬性值 */ @Override public void setDynamicAttribute( String uri, String localName, Object value) throws JspException { //新增屬性名 keys.add( localName ); //新增屬性值 values.add( value ); } } 2) 建立一個*.tld檔案,每個*.tld檔案對應一個標籤庫,每個標籤庫可包含多個標籤 TLD(Tag Library Definition 標籤庫定義)的根元素是taglib,它可以包含多個tag子元素,每個tag元素都定義一個標籤 taglib結構如下 1) tlib-version: 指定該標籤庫實現的內部版本號 2) short-name: 該標籤庫的預設短名 3) uri: 指定該標籤庫的唯一標識URI,JSP頁面中使用標籤庫就是根據該URI屬性來定位標籤庫的 4) tag: 每個tag元素定義一個標籤 4.1) name: 該標籤的名字 4.2) tag-class: 該標籤的處理類 4.3) body-content: 指定標籤體內容 4.3.1) tagdependent: 標籤處理類自己負責處理標籤體 4.3.2) empty: 該標籤只能作為空標籤使用 4.3.3) scriptless: 該標籤可以是靜態HTML元素、表示式語言,但不允許JSP指令碼 4.3.4) JSP: 該標籤可以使用JSP指令碼 4.3.5) dynamic-attributes: 該標籤是否支援動態屬性 JSP2規範的自定義標籤還允許直接將一段"頁面程式碼"作為屬性,這種方式給自定義標籤提供了更大的靈活性,它和普通標籤的區別並不大,對於以"頁面程式碼"作為屬性的自定義標籤來
說,需要注意的是:
1) 標籤處理類中定義型別為JspFragment的屬性,該屬性代表了"頁面片段" 2) 使用標籤庫時,通過<jsp:attribute../>動作指令為標籤庫屬性指定值 除此之外,還可以動態屬性的標籤 <?xml version="1.0" encoding="GBK"?> <taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd" version="2.0"> <tlib-version>1.0</tlib-version> <short-name>mytaglib</short-name> <!-- 定義該標籤庫的URI --> <uri>http://littlehann.cnblogs.com/mytaglib</uri> <!-- 定義第一個標籤 --> <tag> <!-- 定義標籤名 --> <name>helloWorld</name> <!-- 定義標籤處理類 --> <tag-class>lee.HelloWorldTag</tag-class> <!-- 定義標籤體為空 --> <body-content>empty</body-content> </tag> /* 定義第二個標籤,對於有屬性的標籤,需要為<tag../>元素增加<attribute../>子元素,每個attribute子元素定義一個標籤元素。<attribute../>子元素通常還需要指定
如下子元素 1) name: 屬性名,子元素的值是字串內容 2) required: 該屬性是否為必須屬性,true or false 3) fragment: 該屬性是否支援JSP指令碼、表示式等動態內容,true or false
*/ <tag> <!-- 定義標籤名 --> <name>query</name> <!-- 定義標籤處理類 --> <tag-class>lee.QueryTag</tag-class> <!-- 定義標籤體為空 --> <body-content>empty</body-content> <!-- 配置標籤屬性:driver --> <attribute> <name>driver</name> <required>true</required> <fragment>true</fragment> </attribute> <!-- 配置標籤屬性:url --> <attribute> <name>url</name> <required>true</required> <fragment>true</fragment> </attribute> <!-- 配置標籤屬性:user --> <attribute> <name>user</name> <required>true</required> <fragment>true</fragment> </attribute> <!-- 配置標籤屬性:pass --> <attribute> <name>pass</name> <required>true</required> <fragment>true</fragment> </attribute> <!-- 配置標籤屬性:sql --> <attribute> <name>sql</name> <required>true</required> <fragment>true</fragment> </attribute> </tag> <!-- 定義第三個帶標籤體的標籤 --> <tag> <!-- 定義標籤名 --> <name>iterator</name> <!-- 定義標籤處理類 --> <tag-class>lee.IteratorTag</tag-class> <!-- 定義標籤體不允許出現JSP指令碼 --> <body-content>scriptless</body-content> <!-- 配置標籤屬性:collection --> <attribute> <name>collection</name> <required>true</required> <fragment>true</fragment> </attribute> <!-- 配置標籤屬性:item --> <attribute> <name>item</name> <required>true</required> <fragment>true</fragment> </attribute> </tag> <tag> <!-- 定義以"頁面片段"作為屬性的標籤名 --> <name>fragment</name> <!-- 定義標籤處理類 --> <tag-class>lee.FragmentTag</tag-class> <!-- 指定該標籤不支援標籤體 --> <body-content>empty</body-content> <!-- 定義標籤屬性:fragment --> <attribute> <name>fragment</name> <required>true</required> <fragment>true</fragment> </attribute> </tag> <!-- 定義接受動態屬性的標籤 --> <tag> <name>dynaAttr</name> <tag-class>lee.DynaAttributesTag</tag-class> <body-content>empty</body-content> <!-- 指定支援動態屬性 --> <dynamic-attributes>true</dynamic-attributes> </tag> </taglib> 定義了上面的標籤庫定義檔案之後,將標籤庫檔案放在WEB應用的WEB-INF路徑下,WEB容器會自動載入該檔案,則該檔案定義的標籤庫也將生效 3) 在JSP檔案中使用自定義標籤 在JSP頁面中使用標籤庫步驟 1) 匯入標籤庫: 使用taglib編譯指令匯入標籤庫,由URI唯一標識指定標籤庫,並將標籤庫和指定字首關聯起來(即所有使用該字首的標籤將由此標籤庫處理) <%@ taglib uri="tagliburi" prefix="tagPrefix" %> 2) 使用標籤: 在JSP頁面中使用自定義標籤 <tagPrefix:tagName tagAttribute="tagValue" ..> <tagBody/> </tagPrefix:tagName> example1: helloWorld標籤使用 <%@ taglib uri="http://littlehann.cnblogs.com/mytaglib" prefix="mytag"%> <mytag:helloWorld></mytag:helloWorld> example2: QueryTag標籤使用 <%@ taglib uri="http://littlehann.cnblogs.com/mytaglib" prefix="mytag"%> <mytag:query driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/javaee" user="root" pass="32147" sql="select * from news_inf"/> 可以看出自定義標籤庫的作用,以簡單的標籤,隱藏複雜的邏輯 example3: IteratorTag標籤使用 <%@ taglib uri="http://littlehann.cnblogs.com/mytaglib" prefix="mytag"%> <% //建立一個List物件 List<String> a = new ArrayList<String>(); a.add("瘋狂Java"); a.add("www.crazyit.org"); a.add("java"); //將List物件放入page範圍內 pageContext.setAttribute("a" , a); %> <table border="1" bgcolor="#aaaadd" width="300"> <!-- 使用迭代器標籤,對a集合進行迭代 --> <mytag:iterator collection="a" item="item"> <tr> <td>${pageScope.item}</td> <tr> </mytag:iterator> </table> 可以看到,使用iterator標籤遍歷集合元素比使用JSP指令碼遍歷集合元素要優雅,這也是自定義標籤的優勢 example4: fragment標籤使用 <%@ taglib uri="http://littlehann.cnblogs.com/mytaglib" prefix="mytag"%> <mytag:fragment> <!-- 使用jsp:attribute標籤傳入fragment引數 --> <jsp:attribute name="fragment"> <!-- 下面是動態的JSP頁面片段 --> <mytag:helloWorld/> </jsp:attribute> </mytag:fragment> <mytag:fragment> <jsp:attribute name="fragment"> <!-- 下面是動態的JSP頁面片段 --> ${pageContext.request.remoteAddr} </jsp:attribute> </mytag:fragment> example5: dynaAttr標籤使用 <%@ taglib uri="http://littlehann.cnblogs.com/mytaglib" prefix="mytag"%> <mytag:dynaAttr name="crazyit" url="crazyit.org"/> <mytag:dynaAttr 書名="瘋狂Java講義" 價格="99.0" 出版時間="2008年" 描述="Java圖書"/> 可以看到,不管傳入多少屬性,這個標籤都可以處理 JSTL是Sun提供的一套標籤庫,DisplayTag是Apache組織下的一套開源標籤庫,主要用於生成頁面並顯示效果

2. JSP註釋

<%-- 註釋內容 --%>
JSP註釋用於標註在程式開發過程中的開發提示,"不會"輸出到客戶端,即客戶端連註釋符號都看不到 

3. JSP宣告

JSP宣告用於宣告變數和方法,值得注意的是,JSP宣告將會轉換成對應的Servlet(.java檔案)的成員變數或成員方法(牢記這點,後因為我們還會看到在<%..%>中宣告的是區域性成員),
因此JSP宣告依然符合java語法。
<%! 宣告部分 %> 關於JSP的變數宣告 梳理一下,在JSP中宣告變數總共有2中情況: 1) 區域性變數 <% 在JSP(本質是java程式碼)中宣告變數 %>,又因為JSP程式碼是會被Tomcat翻譯成Servlet的物件導向的java程式碼,所以在<% %>中宣告的變數全都是區域性變數(即在程式碼塊中宣告的變數),
它們不能使用private、public、static等修飾 2) 類變數 <%! %> 用這種方式宣告的變數是在類的範圍域中宣告的,屬於類變數,它們可以使用private、public、static等修飾
使用的使用一定要注意
example:
<%! //宣告一個整形變數 public int count; //宣告一個方法 public String info() { return "hello"; } %>

4. JSP表示式

JSP提供了一種輸出表示式值的簡單方法
<%=表示式%> 

example: <%! public int count; %> <%=count++%>

5. JSP指令碼

所有可執行性java程式碼都可以通過JSP指令碼嵌入HTML頁面

example:
<% out.println(new java.util.Date()); %>

6. JSP的動作指令

動作指令與編譯指令不同:
    1) 編譯指令是通知Servlet引擎的處理訊息,是在將JSP編譯成Servlet時起作用
    2) 動作指令只是執行時的動作,通常可替換成JSP指令碼,它只是JSP指令碼的標準化寫法
JSP的動作指令主要有如下7個:

1) jsp:forward: 
執行頁面轉向,將請求的處理轉發到下一個頁面,可以轉發到:
    1) 靜態HTML頁面
    2) 動態的JSP頁面
    3) 容器中的Servlet
語法1:
<jsp:forward page="{relativeURL|<%=expression%>}"/>
語法2:
<jsp:forward page="{relativeURL|<%=expression%>}">
    {<jsp:param.../>}
</jsp:forward>
第二種語法用於在轉發時增加額外的請求引數。增加的請求引數的值可以通過HttpServletRequest類的getParameter()方法獲取
request.getParameter("");
從本質上來說,jsp:forward這個動作指令只是一個內部轉發,即<jsp:forward../>並沒有重新向新頁面傳送了請求,它只是完全採用了新頁面來對使用者生成響應(內部轉發),請求依然是一次
請求,所以請求引數、請求屬性都不會丟失
2) jsp:include: 動態引入一個JSP頁面,它不會匯入被include頁面的編譯指令,僅僅將被匯入頁面的body內容插入本頁面中 <jsp:include page="{relativeURL|<%=expression%>}" flush="true"/> 或者 <jsp:include page="{relativeURL|<%=expression%>}" flush="true"> <jsp:param name="parameterName" value="parameerValue"/> </jsp:include> 這裡要注意和編譯指令的inlcude進行區分 1) 靜態引入(編譯指令) <%@include file="file.jsp"%> 可以將外部檔案嵌入到當前JSP檔案中,同時解析這個頁面中的JSP語句(如果有的話),也就是說,它既可以包含靜態的文字,也可以包含動態的JSP頁面。包含頁面在編譯時將完全包含了被包含
頁面的程式碼。需要指出的是,靜態包含還會將被包含頁面的編譯指令也包含進來,如果兩個頁面的編譯指令衝突,那麼頁面就會出錯
2) 動態引入(動作指令) <jsp:include> 歸納起來,動態匯入和靜態匯入的區別有 1. 靜態匯入是將被匯入頁面的程式碼完全融入,兩個頁面融合成一個整體Servlet 2. 動態匯入則在Servlet中使用include方法來引入被匯入頁面的內容 3. 靜態匯入時被匯入頁面的編譯指令會起作用。 4. 動態匯入時被匯入頁面的編譯指令則失去作用,只是插入被匯入頁面的body內容 5. 動態匯入可以增加額外的引數 實際上,forward動作指令和include動作指令(動態引入)十分相似,它們都採用方法來引入目標頁面 forward指令使用_jspx_page_context.forward()方法來引入目標頁面 include指令使用org.apache.jasper.runtime.JspRuntimeLibrary.include()方法來引入目標頁面 區別在於,執行forward時,被forward的頁面將完全代替原有的頁面,而執行include時,被include的頁面只是插入原有頁面 即forward拿目標頁面代替原有頁面,而include則拿目標頁面插入原有頁面 3) JavaBean相關 3.1) jsp:useBean: 在JSP頁面中初始化一個JavaBean的例項 <jsp:useBean id="name" class="Classname" scope="page|request|session|application"/> 3.1.1) id: JavaBean的例項名 3.1.2) class: 確定JavaBean的實現類 3.1.3) scope: 指定JavaBean例項的作用範圍 3.1.3.1) page: 該JavaBean例項僅在該頁面有效 3.1.3.2) request: 在本次請求有效 3.1.3.3) session: 在本次session內有效 3.1.3.4) application: 在本應用中一直有效 3.1.4) 3.2) jsp:setProperty: 設定JavaBean例項的屬性值 <jsp:setProperty name="BeanName" property="propertyName" value="value" /> 3.2.1) name: JavaBean的例項名 3.2.2) property: 確定需要設定的屬性名 3.2.3) value: 確定需要設定的屬性值 3.3) jsp:getProperty: 輸出JavaBean例項的屬性值 <jsp:getProperty name="BeanName" property="propertyName" /> 3.3.1) name: 確定需要輸出的JavaBean例項名 3.3.2) property: 確定需要輸出的屬性名 這三個指令都是與JavaBean相關的指令,如果多個JSP頁面中需要重複使用某段程式碼,我們可以把這段程式碼定義成java類的方法,然後讓多個JSP頁面呼叫該方法即可,這樣可以達到較好的程式碼
複用 在.jsp頁面中我們可以這樣編碼
<!-- 建立lee.Person的例項,該例項的例項名為p1 --> <jsp:useBean id="p1" class="lee.Person" scope="page"/> <!-- 設定p1的name屬性值 --> <jsp:setProperty name="p1" property="name" value="wawa"/> <!-- 設定p1的age屬性值 --> <jsp:setProperty name="p1" property="age" value="23"/> <!-- 輸出p1的name屬性值 --> <jsp:getProperty name="p1" property="name"/><br/> <!-- 輸出p1的age屬性值 --> <jsp:getProperty name="p1" property="age"/> 從程式碼中可以看到,我們使用了useBean、setProperty、getProperty來操作JavaBean的方法,同時我們需要明白的是,對於property="name",在JavaBean中提供了setName()、
getName()方法來操作,property="age"也是同理 程式碼中對應的JavaBean的Person類原始碼如下 package lee; public class Person { private String name; private int age; //無引數的構造器 public Person() { } //初始化全部屬性的構造器 public Person(String name , int age) { this.name = name; this.age = age; } //name屬性的setter和getter方法 public void setName(String name) { this.name = name; } public String getName() { return this.name; } //age屬性的setter和getter方法 public void setAge(int age) { this.age = age; } public int getAge() { return this.age; } } 4) plugin指令 plugin指令主要用於下載伺服器端的JavaBean、或Applet到客戶端執行。由於程式在客戶端執行,因此客戶端必須安裝虛擬機器 5) param指令 param指令用於設定引數值,這個指令本身不能單獨使用,因為單獨的param沒有實際意義。param指令可以與以下三個指令結合使用 5.1) jsp:include 當與include指令結合使用時,param指令用於將引數值傳入被匯入的頁面 5.2) jsp:forward 當與forward指令結合使用時,param指令用於將引數值傳入被轉向的頁面 5.3) jsp:plugin 當與plugin指令結合使用時,用於將引數傳入頁面中的JavaBean例項或Applet例項 <jsp:param name="paramName" value="paramValue"/> */

7. JSP指令碼中的內建物件

JSP指令碼中包含內建物件,這些內建物件都是Servleet API介面的例項,JSP規範對它們進行了預設初始化(由JSP頁面對應的Servlet的_jspService()方法來建立這些例項)。即它們已經是對
象了,可以直接在JSP指令碼中使用了 內建物件依次如下:
1) application: javax.servlet.ServletContext的例項,該例項代表JSP所屬的WEB應用本身,因此可以使用application來操作WEB相關的資料,application物件通常有如下兩個作用: 1.1) 在整個WEB應用的多個JSP、Servlet之間共享資料 application通過setAttribute(String attrName, Object value)方法將一個值設定成application的attrName屬性,該屬性的值對整個WEB應用有效,因此該WEB應用的每
個JSP頁面或Servlet都可以訪問該屬性,訪問屬性的方法為getAttribute(String attrName)
1) put-application.jsp <% application.setAttribute("counter",String.valueOf(++i)); %> 2) get-application.jsp <%=application.getAttribute("counter")%> 3) GetApplication.java ServletContext sc = getServletConfig().getServletContext(); out.println(sc.getAttribute("counter")); 因為application代表的是整個WEB應用,因此可以在JSP、Servlet之間共享資料,由於在Servlet中並沒有application內建物件,所以需要獲取該web應用的ServletContext
例項,每個web應用只有一個ServletContext例項,而在JSP中可以直接通過application內建物件訪問該例項
1.2) 訪問WEB應用的配置引數 除了共享資料,application還可用於從web.xml中獲得WEB應用的配置引數 //從配置引數中獲取驅動 String driver = application.getInitParameter("driver"); 從以上的程式碼可以看到,可以使用application的getInitParameter(String paramName)來獲取WEB應用的配置引數,這些引數應該在web.xml檔案中使用context-param元素
配置,每個<context-param../>元素配置一個引數 <context-param> <param-name>driver</param-name> <param-value>com.mysql.jdbc.Driver</param-value> </context-param> 2) config物件 javax.servlet.ServletConfig類的例項,config物件代表當前JSP配置資訊 <%=config.getServletName()%> 因為所有的JSP頁面都有相同的名字: jsp,所以這段JSP程式碼永遠輸出jsp 實際上,我們也可以在web.xml檔案中配置JSP,這樣就可以為JSP頁面指定配置資訊,併為JSP頁面指定一個虛擬路徑 1) configTest2.jsp <!-- 輸出該JSP名為name的配置引數 --> name配置引數的值:<%=config.getInitParameter("name")%><br/> <!-- 輸出該JSP名為age的配置引數 --> age配置引數的值:<%=config.getInitParameter("age")%> 2) web.xml <servlet> <!-- 指定Servlet名字 --> <servlet-name>config</servlet-name> <!-- 指定將哪個JSP頁面配置成Servlet --> <jsp-file>/configTest2.jsp</jsp-file> <!-- 配置名為name的引數,值為yeeku --> <init-param> <param-name>name</param-name> <param-value>yeeku</param-value> </init-param> <!-- 配置名為age的引數,值為30 --> <init-param> <param-name>age</param-name> <param-value>30</param-value> </init-param> </servlet> <servlet-mapping> <!-- 指定將config Servlet配置到/config URL--> <servlet-name>config</servlet-name> <url-pattern>/config</url-pattern> </servlet-mapping> 3) exception物件 exception物件是Throwable的例項,代表JSP指令碼中產生的錯誤和異常,是JSP異常機制的一部分。在JSP指令碼中無須處理異常,即使該異常是checked異常,JSP指令碼包含的異常都可以交
給錯誤處理頁面處理,exception物件也僅在異常處理頁面中才有效。 開啟普通的JSP頁面所生成的Servlet類,可以看到如下程式碼段
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response) throws java.io.IOException, javax.servlet.ServletException { .. try { response.setContentType("text/html; charset=GBK"); .. out.write(' '); } catch (java.lang.Throwable t) { .. if (_jspx_page_context != null) _jspx_page_context.handlePageException(t); else throw new ServletException(t); } finally { _jspxFactory.releasePageContext(_jspx_page_context); } } 從以上程式碼可以看出,JSP指令碼和靜態HTML部分都已經轉換成_jspService()方法裡的執行型程式碼,這就是JSP指令碼無須處理異常的原因,因為這些指令碼都已經被包含在try塊中了。一旦try
塊中捕捉到JSP指令碼的異常,並且_jspx_page_context不為null,就會由該物件來處理異常。_jspx_page_context對異常的處理也非常簡單: 如果該頁面的page指令指定了errorPage屬性
,則將請求forward到errorPage屬性指定的頁面,否則使用系統頁面來輸出異常資訊
4) out物件 out物件代表一個頁面輸出流,通常用於在頁面刪輸出變數值、及常量。所有使用out的地方,都可以使用輸出表示式來代替,而且輸出表示式更加簡潔。從底層原理來看,<%=..%>的本質就
是out.write(..);
5) pageContext物件 pageContext物件代表頁面上下文,該物件主要用於訪問JSP之間的共享資料。使用pageContext可以訪問page、request、session、application範圍的變數。 pageContext是javax.servlet.jsp.PageContext類的例項,它提供了 getAttribute(String name, int scope)來取得指定範圍內的name屬性,其中scope可以是如下值: 1) PageContext.PAGE_SCOPE(對應於page範圍,預設值) 2) PageContext.REQUEST_SCOPE(對應於requsst範圍) 3) PageContext.SESSION_SCOPE(對應於session範圍) 4) PageContext.APPLICATION_SCOPE(對應於application範圍) 與getAttribute相對的,PageContext內建物件自然也有一個setAttribute()方法,用於將指定變數放入page、request、session、application範圍內 <% //使用pageContext設定屬性,該屬性預設在page範圍內 pageContext.setAttribute("page","hello"); //下面獲取各屬性所在的範圍: out.println("page變數所在範圍:" + pageContext.getAttributesScope("page") + "<br/>"); %> 除此之外,pageContext還可用於獲取其他內建物件,pageContext物件還包含如下方法 1) ServletRequest getRequest(): 獲取request物件 2) ServletResponse getResponse(): 獲取response物件 3) ServletConfig getServletConfig(): 獲取config物件 4) ServletContext getServletContext(): 獲取application物件 5) HttpSession getSession(): 獲取session物件 因此,pageContext物件是一個很重要的物件,一旦在JSP、Servlet程式設計中獲取了pageContext物件,就可以通過它提供的相應方法來獲取其他內建物件 6) request物件 request物件是JSP中重要的物件,每個request物件封裝著一次使用者請求,並且所有的請求引數都被封裝在request物件中,因此request物件是獲取"請求引數"的重要途徑。除此之外,
request可代表本次請求的範圍,所以還可用於操作request範圍的屬性 和request物件相關的功能有:
1) 獲取請求頭/請求引數 request是HttpServletRequest介面的例項,它提供瞭如下幾個方法來獲得"請求引數"(GET、POST、COOKIE) 1.1) String getParameter(String paramName): 獲取paramName引數的值 1.2) Map getParameterMap(): 獲取所有請求引數名和引數值所組成的Map物件 1.3) Enumeration getParameterNames(): 獲取所有請求引數名和引數值所組成的Enumeration物件 1.4) String[] getParameterValues(String name): 獲取引數name的值,如果有同名的多個,則返回陣列 HttpServletRequest提供瞭如下方法來訪問HTTP請求頭 1.1) String getHeader(String name): 獲取指定HTTP頭的引數值 1.2) java.util.Enumeration<String> getHeaderNames(): 獲取所有請求頭的名稱 1.3) java.util.Enumeration<String> getHeaders(String name): 獲取指定請求頭的所有值 1.4) int getIntHeader(String name): 獲取指定請求頭的值,並轉化為整數值返回 2) 操作request範圍的屬性 HttpServletRequest還包含如下兩個方法,用於設定和獲取request範圍的屬性 2.1) setAttribute(String attName, Object attValue): 將attValue設定成request範圍的屬性(用於JSP頁面間共享變數) 2.2) Object getAttribute(String attName): 獲取request範圍的屬性 3) 執行forward、或include request還有一個功能是執行forward和include,也就是代替JSP所提供的forward和include動作指令。 HttpServletRequest類提供了一個: RequesDispatcher getRequestDispatcher(String path): 1) path為希望forward或include的目標路徑 返回了一個RequesDispatcher物件,它提供如下兩個方法: 1) forward(ServletRequest request, ServletResponse response): 執行forward getRequestDispatcher("/a.jsp").forward(request, response); 2) include(ServletRequest request, ServletResponse response): 執行include getRequestDispatcher("/a.jsp").include(request, response); 7) response物件 response代表伺服器對客戶端的響應。大部分時候,使用out(頁面輸出流)生成響應更簡單。但out是JspWriter的例項,JspWriter是Writer的子類,而Writer是字元流,無法輸出非字
符資料。如果需要在JSP中生成一副點陣圖、PDF文件,則必須使用response作為響應輸出
7.1) response響應生成非字元響應 response是HttpServletResponse介面的例項,該介面提供了一個getOutputStream()方法,該方法返回響應輸出"位元組流" <%-- 通過contentType屬性指定響應資料是圖片 --%> <%@ page contentType="image/jpeg" language="java"%> <%@ page import="java.awt.image.*,javax.imageio.*,java.io.*,java.awt.*"%> <% //建立BufferedImage物件 BufferedImage image = new BufferedImage(340, 160, BufferedImage.TYPE_INT_RGB); //以Image物件獲取Graphics物件 Graphics g = image.getGraphics(); //使用Graphics畫圖,所畫的影象將會出現在image物件中 g.fillRect(0,0,400,400); //設定顏色:紅 g.setColor(new Color(255,0,0)); //畫出一段弧 g.fillArc(20, 20, 100,100, 30, 120); //設定顏色:綠 g.setColor(new Color(0 , 255, 0)); //畫出一段弧 g.fillArc(20, 20, 100,100, 150, 120); //設定顏色:藍 g.setColor(new Color(0 , 0, 255)); //畫出一段弧 g.fillArc(20, 20, 100,100, 270, 120); //設定顏色:黑 g.setColor(new Color(0,0,0)); g.setFont(new Font("Arial Black", Font.PLAIN, 16)); //畫出三個字串 g.drawString("red:climb" , 200 , 60); g.drawString("green:swim" , 200 , 100); g.drawString("blue:jump" , 200 , 140); g.dispose(); //將影象輸出到頁面的響應 ImageIO.write(image , "jpg" , response.getOutputStream()); %> 從程式碼中可以看到幾個關鍵點: 1) 設定了伺服器響應資料是image/jpeg,這表明伺服器響應是一張JPG圖片 2) 最後的響應輸出流是一個圖片的位元組流 也可以在其他HTML頁面中使用img標籤來顯示這張圖片 <img src="img.jsp"> 7.2) 重定向 重定向是response的另外一個用處,要注意的是,和forward這種內部轉接"不同"的是,response的重定向會丟失所有的原始請求引數和request範圍的屬性,因為重定向將生成第二
次請求,自然與前一次請求不在同一個request範圍內。 HttpServletResponse提供了一個sendRedirect(String path)方法,該方法用於
"重定向"到path資源,即"重新"向path資源傳送請求 7.3) 增加Cookie 增加Cookie也是使用response內建物件完成的,增加Cookie的步驟如下 1) 建立Cookie例項,建構函式Cookie(String name, String value) 2) 設定Cookie的生命週期,即該Cookie在多長時間內有效 3) 向客戶端寫Cookie,void addCookie(Cookie cookie): 增加Cookie <% //獲取請求引數 String name = request.getParameter("name"); //以獲取到的請求引數為值,建立一個Cookie物件 Cookie c = new Cookie("username" , name); //設定Cookie物件的生存期限 c.setMaxAge(24 * 3600); //向客戶端增加Cookie物件 response.addCookie(c); %> 獲取客戶端傳送的Cookie的方法 <% //獲取本站在客戶端上保留的所有Cookie Cookie[] cookies = request.getCookies(); //遍歷客戶端上的每個Cookie for (Cookie c : cookies) { //如果Cookie的名為username,表明該Cookie是我們需要訪問的Cookie if(c.getName().equals("username")) { out.println(c.getValue()); } } %> 要注意的是: 1) 使用Cookie物件必須設定生存週期,否則Cookie將會隨瀏覽器關閉而自動消失(session cookie) 2) 如果要存入中文Cookie,則需要使用java.net.URLEncoder進行編碼,在獲取時用java.net.URLDncoder進行解碼 8) session物件 session物件代表一次使用者會話,session範圍內的屬性可以在多個頁面的跳轉之間共享。session物件是HttpSession的例項,它有如下兩個常用的方法: 8.1) setAttribute(String attName, Object attValue): 設定session範圍內attName屬性的值為attValue 8.2) getAttribute(String attName): 返回session範圍內attName屬性的值 使用session物件要注意的是: 考慮session本身的目的,通常只應該把與使用者會話狀態相關的資訊放入session範圍內。不要僅僅為兩個頁面之間交換資訊,就將資訊放入session範圍內。如果僅僅是為了頁面間交換信
息,可以將資訊放入request範圍內,然後forward請求即可。 除此之外,session機制通常用於儲存客戶端的狀態資訊,這些狀態資訊需要儲存到web伺服器的硬碟上,所以要求session裡的屬性值必須是可序列化的,否則將引起
"不可序列化異常"
即session的屬性值可以是任何可序列化的java物件

 

 

 4. Servlet基礎語法

我們知道,JSP的本質就是Servlet,開發者把編寫好的JSP頁面部署在WEB容器中之後,WEB容器會將JSP編譯成對應的Servlet。

Servlet和JSP的區別在於:

1) Servlet中沒有內建物件,原來在JSP中的內建物件都必須由程式顯示建立。JSP是Servlet的一種簡化,使用JSP只需要完成程式設計師需要輸出到客戶端的內容,至於JSP指令碼如何嵌入一個類中,
由JSP容器完成
2) 對於靜態HTML標籤,Servlet都必須使用頁面輸出流逐行輸出。而Servlet則是一個完整的java類,這個類的service()方法用於生成對客戶端的響應

0x1: Servlet的配置

配置需要的準備條件如下:

1. 編輯好的Servlet原始碼檔案並不能響應使用者請求,還必須將其編譯成class檔案。將編譯好的.class檔案放在WEB-INF/classes路徑下,如果Servlet有包,則還應該將class檔案放在對
應的包路徑下
2. 為了讓Servlet能響應使用者請求,還必須將Servlet配置在web應用中,配置Servlet有兩種方式 1) 在Servlet類中使用@WebServlet Annotation進行配置 使用@WebServlet時可制定如下常用屬性 1.1) asyncSupported: 宣告Servlet是否支援非同步操作模式,等價於<async-supported>標籤 1.2) displayName: 該Servlet的顯示名,通常配合工具使用,等價於<display-name>標籤 1.3) initParams: 指定一組Servlet初始化引數,等價於<init-param>標籤 1.4) loadOnStartup: 指定Servlet的載入順序,等價於<load-on-startup>標籤 1.5) name: 指定Servlet的name屬性,等價於<servlet-name>,如果沒有顯式指定,則該Servlet的取值即為類的全限定名 1.6) urlPatterns: 指定一組Servlet的URL匹配模式(虛擬路徑)。等價於<url-pattern>標籤 1.7) value: 該屬性等價於urlPatterns屬性。兩個屬性不能同時使用 example: @WebServlet(name="firstServlet", urlPatterns={"/firstServlet"}) 需要注意的是,如果打算採用Annotation來配置Servlet,需要注意如下兩點: 1) 不要在web.xml檔案的根元素<web-app../>中指定metadata-complete="true",因為當該屬性值為true時,該web應用不會載入Annotation配置的WEB元件(如Servlet、
Filter、Listener等)
2) 不要在web.xml檔案中配置該Servlet 2) 通過在web.xml檔案中進行配置 2.1) 配置Servlet的名字: 對應web.xml檔案中的<servlet/>元素 2.2) 配置Servlet的URL: 對應web.xml檔案中的<servlet-mapping/>元素。這一步是可選的。但如果沒有為Servlet配置URL(虛擬路徑),則該Servlet不能響應使用者請求 example: <servlet> <!-- 指定Servlet的名字,相當於指定@WebServlet的name屬性 --> <servlet-name>firstServlet</servlet-name> <!-- 指定Servlet的實現類 --> <servlet-class>lee.FirstServlet</servlet-class> </servlet> <!-- 配置Servlet的URL --> <servlet-mapping> <!-- 指定Servlet的名字 --> <servlet-name>firstServlet</servlet-name> <!-- 指定Servlet對映的URL地址,相當於指定@WebServlet的urlPatterns屬性--> <url-pattern>/aa</url-pattern> </servlet-mapping> 需要明白的,Annotation和web.xml配置,只要其中一種即可完成Servlet的配置

0x2: JSP/Servlet的生命週期

JSP的本質就是Servlet,當Servlet在容器中執行時,其例項的建立及銷燬等都不是由程式設計師決定的,而是由WEB容器進行控制的。
建立Servlet例項有兩個時機

1. 客戶端第一次請求某個Servlet時,系統建立該Servlet的例項: 大部分的Servlet都是這種情況
2. WEB應用啟動時立即建立Servlet例項,即load-on-startup Servlet

每個Servlet的執行都遵循如下生命週期

1. 建立Servlet例項
2. WEB容器呼叫Servlet的init方法,對Servlet進行初始化
3. Servlet初始化之後,將一直存在於容器中,用於響應客戶端請求:
    1) 如果客戶端傳送GET請求,容器呼叫Servlet的doGet方法處理並響應請求
    2) 如果客戶端傳送POST請求,容器呼叫Servlet的doPost方法處理並響應請求
    3) 或者統一用service()方法處理來響應使用者請求
4. WEB容器決定銷燬Servlet時,先呼叫Servlet的destroy方法,通常在關閉web應用之前銷燬Servlet

0x3: Filter介紹

Filter從本質上來說和Servlet很相似,它主要用於對用於請求(HttpServletRequest)進行預處理、以及對使用者響應(HttpServletResponse)進行後處理,和linux上的iptables類似,是一種典型的"處理鏈"機制。
Filter完整執行的流程是:

1) Filter對用於請求(HttpServletRequest)進行預處理
它使使用者可以使用序列的方式在request到達servlet之前預處理,即改變一個Request

2) 接著將請求交給Servlet進行處理並生成響應

3) 最後Filter再對伺服器響應(HttpServletResponse)進行後處理
它使使用者可以使用序列的方式在request離開servlet時處理response,即修改一個response

Filter可以實現的功能包括

1. 在servlet被呼叫之前截獲 
2. 在servlet被呼叫之前檢查servlet request 
3. 根據需要修改request頭和request資料 
4. 根據需要修改response頭和response資料 
5. 在servlet被呼叫之後截獲

使用Filter程式設計的完整流程是:

1. 建立Filter類
建立Filter必須實現javax.servlet.Filter介面,在該介面中定義瞭如下3個方法
    1) void init(FilterConfig config): Filter的初始化
    2) void destroy(): 用於Filter銷燬前,完成資源的回收等工作
    3) void doFilter(ServletRequest request, ServletResponse response, FilterChain chain): 實現過濾功能,該方法負責對每個請求、及響應增加額外的處理
example:
package lee;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.*;

@WebFilter(filterName="log", urlPatterns={"/*"})
public class LogFilter implements Filter 
{
    //FilterConfig可用於訪問Filter的配置資訊
    private FilterConfig config;
    //實現初始化方法
    public void init(FilterConfig config)
    {
        this.config = config; 
    }
    //實現銷燬方法
    public void destroy()
    {
        this.config = null; 
    }
    //執行過濾的核心方法
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,ServletException
    {
        //---------下面程式碼用於對使用者請求執行預處理---------
        //獲取ServletContext物件,用於記錄日誌
        ServletContext context = this.config.getServletContext(); 
        long before = System.currentTimeMillis();
        System.out.println("開始過濾...");
        //將請求轉換成HttpServletRequest請求
        HttpServletRequest hrequest = (HttpServletRequest)request;
        //輸出提示資訊
        System.out.println("Filter已經截獲到使用者的請求的地址: " + hrequest.getServletPath());
        //Filter只是鏈式處理,請求依然放行到目的地址
        chain.doFilter(request, response);
        //---------下面程式碼用於對伺服器響應執行後處理---------
        long after = System.currentTimeMillis();
        //輸出提示資訊
        System.out.println("過濾結束");
        //輸出提示資訊
        System.out.println("請求被定位到" + hrequest.getRequestURI() + "   所花的時間為: " + (after - before)); 
    } 
}
從程式碼中可以看到,doFilter()方法中"請求預處理""響應後處理"的分界線是chain.doFilter()

2. 配置Filter
配置Filter和配置Servlet非常類似,需要配置如下兩部分:
    1) 配置Filter名
    2) 配置Filter攔截URL模式: Servlet通常只配置一個URL,而Filter可以同時攔截多個請求的URL。因此在配置Filter的URL模式時通常會使用"模式字串"(正則字串),使得
Filter可以攔截多個請求
2.1) 在Filter類中通過Annotation進行配置 2.1.1) asyncSupported: 宣告Filter是否支援非同步操作模式 2.1.2) dispatcherTypes: 指定該Filter僅對哪種dispatcher模式的請求進行過濾 2.1.2.1) ASYNC 2.1.2.2) ERROR 2.1.2.3) FORWARD 2.1.2.4) INLCUDE 2.1.2.5) REQUEST 這5種組合可以以OR的形式進行組合,dispatcherTypes的預設值是"同時"過濾5種模式的請求 2.1.3) displayName: 該Filter的顯示名 2.1.4) filterName: 指定該Filter的名稱 2.1.5) initParams: 指定一組Filter配置引數 2.1.6) servletName: 指定多個Servlet的名稱,用於指定該Filter僅對這幾個Servlet執行過濾 2.1.7) urlPatterns: 指定一組Filter的URL匹配模式(虛擬路徑) 2.1.8) value: 該屬性等價於urlPatterns屬性。兩個屬性不能同時使用 2.2) 在web.xml檔案中通過配置檔案進行配置 <filter> <!-- Filter的名字,相當於指定@WebFilter的filterName屬性 --> <filter-name>log</filter-name> <!-- Filter的實現類 --> <filter-class>lee.LogFilter</filter-class> </filter> <!-- 定義Filter攔截的URL地址 --> <filter-mapping> <!-- Filter的名字 --> <filter-name>log</filter-name> <!-- Filter負責攔截的URL,相當於指定@WebFilter的urlPatterns屬性 --> <url-pattern>/*</url-pattern> </filter-mapping> 3. 使用Filter Filter對應的.class檔案被載入後,Filter就會根據設定的URL模式進行響應的預處理、後處理

Tomcat Filter的函式呼叫流程為

1. setFilterConfig()方法
伺服器每次只呼叫一次準備filter的處理
 
2. doFilter()方法
呼叫多次,用於每次以處理不同的請求。FilterConfig介面有方法可以找到filter名字及初始化引數資訊。伺服器可以設定FilterConfig為空來指明filter已經終結。
每一個filter從doFilter()方法中得到當前的request及response。在這個方法裡,可以進行任何的針對request及 response的操作.(包括收集資料,包裝資料等)

3. chain.doFilter()方法
filter呼叫chain.doFilter()方法把控制權交給下一個filter。一個filter在doFilter()方法中結束。如果一個filter想停止request處理而獲得對response的完全的控制,那它可以不呼叫下一個filter,即這是一個鏈式的操作

Filter和Servlet的異同

從本質上來說,Filter和Servlet很相似,Filter裡的doFilter()方法裡的程式碼就是從多個Servlet的service()方法裡抽取的通用程式碼,通過使用Filter可以實現更好的程式碼複用
(類似PHP中的auto_prepend_file、auto_append_file) 假設系統有包含多個Servlet,這些Servlet都需要進行一些通用處理,比如
1) 許可權控制: 在預處理中判斷使用者是否登入,從而決定是否重定向到初始登入頁面 2) 記錄日誌: 將使用者的訪問記錄記錄到日誌中 3) URL Rewrite實現網站偽靜態 所謂偽靜態,是將*.jsp、*.php、這種動態URL偽裝成靜態HTML頁面,目的是提供搜尋引擎的收錄率,我們可以通過Filter攔截所有發向*.html請求,然後按某種規則將請求forward到實際的
*.jsp頁面 3.1) 下載Url Rewrite: http://www.tuckey.org/urlrewrite/ 3.2) 將urlrewrite-3.2.0.jar放到WEB-INF\lib目錄下 3.3) 配置WEB-INF\web.xml <filter> <filter-name>UrlRewriteFilter</filter-name> <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class> </filter> <!-- 配置Url Rewrite的Filter攔截所有請求 --> <filter-mapping> <filter-name>UrlRewriteFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 3.4) 配置WEB-INF\urlrewrite.xml <urlrewrite> <rule> <!-- 所有配置如下正規表示式的請求 --> <from>/userinf-(\w*).html</from> <!-- 將被forward到如下JSP頁面,其中$1代表上面第一個正規表示式所匹配的字串 --> <to type="forward">/userinf.jsp?username=$1</to> </rule> </urlrewrite> 3.5) 訪問偽靜HTML頁面: http://localhost:8080/urlrewrite/userinf-LittleHann.html

0x4: Listener介紹

當WEB應用在WEB容器中執行時,WEB應用內部會不斷地發生各種事件:

1) web應用被啟動
2) web應用被停止
3) 使用者session開始
4) 使用者session結束
5) 使用者請求到達

通常來說,這些web事件對開發者是透明的
實際上,Servlet API提供了大量監聽器和監聽WEB應用內部事件,從而允許當WEB內部事件發生時自動回撥"事件監聽器內的方法"

使用Listener的步驟如下:

1. 實現Listener類
監聽不同的WEB事件的監聽器也不相同。WEB事件監聽器介面如下
    1) ServletContextListener: 用於監聽WEB應用的啟動和關閉
        1.1) contextInitialized(ServletContextEvent sce)
        1.2) contextDestroyed(ServletContextEvent sce)
    2) ServletContextAttributeListener: 用於監聽ServletContext範圍(application)內屬性的改變
        2.1) attributeAdded(ServletContextAttributeEvent event) 
        2.2) attributeRemoved(ServletContextAttributeEvent event)
        2.3) attributeReplaced(ServletContextAttributeEvent event)
    3) ServletRequestListener: 用於監聽使用者請求
        3.1) requestInitialized(ServletRequestEvent sre) 
        3.2) requestDestroyed(ServletRequestEvent sre) 
    由於實現了ServletRequestListener介面的監聽器可以非常方便地監聽到每次請求的建立、銷燬,因此web應用可通過該介面的監聽器來監聽訪問該應用的每個請求,從而實現系統日誌
    4) ServletRequestAttributeListener: 用於監聽ServletRequest範圍(request)內屬性的改變
        4.1) attributeAdded(ServletRequestAttributeEvent event) 
        4.2) attributeRemoved(ServletRequestAttributeEvent event)
        4.3) attributeReplaced(ServletRequestAttributeEvent event) 
    5) HttpSessionListener: 用於監聽使用者session的開始和結束
        5.1) sessionCreated(HttpSessionEvent se) 
        5.2) sessionDestroyed(HttpSessionEvent se)
    實現HttpSessionListener介面的監聽器可以監聽每個用於會話的開始和斷開,因此應用可以通過該監聽器監聽系統的線上使用者
    6) HttpSessionAttributeListener: 用於監聽HttpSession範圍(session)內屬性的改變
        6.1) attributeAdded(HttpSessionBindingEvent se) 
        6.2) attributeRemoved(HttpSessionBindingEvent se) 
        6.3) attributeReplaced(HttpSessionBindingEvent se) 
2. 配置Listener
配置Listener只要向web應用註冊Listener實現類即可,無須配置引數,相對較簡單
    1) 使用@WebListener修飾Listener實現類
    2) 在web.xml文件中使用<listener../>元素進行配置
    <listener>
        <!-- 指定Listener的實現類 -->
        <listener-class>lee.GetConnListener</listener-class>
    </listener>
3. 在指定事件發生時執行相應的函式,我們在實現相應的介面時就需要實現相應的函式

 

Copyright (c) 2014 LittleHann All rights reserved