自從03年發布了jsp2.0之后,新增了一些額外的特性,這些特性使得動態網頁設計變得更加容易。jsp2.0以后的版本統稱jsp2。主要的新增特性有如下幾個:
- 直接配置jsp屬性
- 表達式語言(EL)
- 標簽文件(Tag File)
一、直接配置jsp屬性
原先我們對於jsp頁面的一些屬性的配置需要使用編譯指令page在頁面的頭部指定,這就會導致在多個jsp頁面中都做了同樣的事情。jsp2為我們提供了一種方式,可以在web.xml中統一指定某個或者某些jsp頁面的一些屬性配置。主要的可配置的jsp屬性有:
<el-ignored></el-ignored> ---指定該jsp頁面是否支持表達式語言
<page-encoding></page-encoding> ---指定該jsp頁面的編碼方式
<scripting-invalid></scripting-invalid> --指定該頁面是否允許jsp腳本
<include-prelude></include-prelude> --向指定的頁面隱式的包含外部文件(引入的位置在指定頁面的頭部)
<include-coda></include-coda> --向指定的頁面隱式的包含外部文件(引入的位置在指定頁面的底部)
在web.xml中我們使用元素jsp-config來直接配置jsp屬性,每個jsp-config元素下,可以由多個jsp-property-group元素,它就是用來具體配置某個或者某些jsp屬性的,具體看下面的演示:
<jsp-config>
<jsp-property-group>
<url-pattern>/index.jsp</url-pattern>
<el-ignored>false</el-ignored>
<page-encoding>UTF-8</page-encoding>
<scripting-invalid>false</scripting-invalid>
<include-prelude>/top.jsp</include-prelude>
<include-coda>/bottom.jsp</include-coda>
</jsp-property-group>
<jsp-property-group>
<url-pattern>/index2.jsp</url-pattern>
<el-ignored>true</el-ignored>
<page-encoding>UTF-8</page-encoding>
<scripting-invalid>true</scripting-invalid>
<include-prelude>/top.jsp</include-prelude>
<include-coda>/bottom.jsp</include-coda>
</jsp-property-group>
</jsp-config>
其中url-pattern元素用來指定哪些jsp頁面將被應用以下配置,可以是單個jsp頁面,也可以是一組jsp頁面。el-ignored元素指定該頁面是否支持表達式語言(接下來會說),默認是false表示支持EL。page-encoding元素指定了頁面的編碼方式,等效於jsp頁面page編譯指令中contentType屬性中charset部分。scripting-invalid元素指定了頁面是否支持jsp腳本,默認為false表示支持。include-prelude和include-coda元素我們分別隱式引用了外部文件,一個放在原頁面的頭部,另一個則是放在尾部。我們看看這兩個頁面:
<%@ page contentType="text/html;charset=UTF-8" language="java" import="java.util.*" %>
<html>
<head>
<title>top.jsp</title>
</head>
<body>
<p>這是頭部的內容</p>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" import="java.util.*" %>
<html>
<head>
<title>bottom.jsp</title>
</head>
<body>
<p>這是底部的內容</p>
</body>
</html>
<%@ page contentType="text/html;charset=UTF-8" language="java" import="java.util.*" %>
<html>
<head>
<title>index.jsp</title>
</head>
<body>
<p>這是index頁面的內容</p>
<p>這是index頁面的內容</p>
<p>這是index頁面的內容</p>
<p>這是index頁面的內容</p>
<p>這是index頁面的內容</p>
</body>
</html>
結果如下:

查看網頁源碼可以看到:

可以看到這兩個元素的作用和我們jsp中的編譯指令include十分相似,只是我們的include編譯指令可以自己選擇引入的外部文件放在被引入的jsp頁面中的位置,而這兩個元素則一個是引入到當前jsp頁面頭部,一個則是引入到當前jsp頁面的底部。
二、表達式語言(EL)
表達式語言是一種簡化了的數據訪問方式,使用它我們可以用簡單的語法來實現對數據的訪問。在jsp2中,建議使用表達式語言使得jsp頁面格式一致,而避免使用jsp腳本。EL的使用語法是:${expression}。在了解如何使用之前,我們先了解一下他的內置對象,這些對象也是我們使用EL的關鍵。
pageContext: 獲取當前頁面的context對象,如jspcontext,servletcontext
pageScope:用於獲取page范圍內的屬性的值
requestScope:用於獲取request范圍內的屬性的值
sessionScopt:用於獲取session范圍內的屬性的值
applicationScope:用於獲取application范圍內的屬性的值
param:用於獲取請求參數的值
paramValues:用於獲取請求參數的集合(數組形式)
initParam:用於獲取請求web應用的初始化參數(web.xml中的)
cookie:用於獲取指定的cookie
從EL的內置對象看,基本上前台需要的數據都是可以獲取到的。下面演示如何使用EL獲取數據。
<%@ page contentType="text/html;charset=UTF-8" language="java" import="java.util.*" %>
<html>
<head>
<title>index</title>
</head>
<body>
<%
request.setAttribute("a","aaaaa");
session.setAttribute("b","bbbbb");
%>
${requestScope.a} <!--或者${requestScope["a"]}-->
${sessionScope.b}
</body>
</html>

以上的演示是EL的很簡單的使用方法,也是利用了它的內置對象來完成對數據的訪問獲取。下面我們看看如何通過EL語法調用自定義函數,可以說擴充了調用自定義函數的功能使得EL更加靈活。但是允許EL調用的函數都必須是靜態的(因為如果非靜態的就需要創建對象來調用,違背了EL設計的初衷)。自定義函數主要有以下三個步驟:
- 定義一個具有靜態方法的類,並定義一些靜態方法
- 在標簽庫中配置可供調用的方法
- 在jsp頁面中使用自定義方法
第一步比較簡單,就是定義一個具有靜態方法的類,第二步有點像我們的自定義標簽的過程,創建一個標簽庫,在taglib元素下,定義一個function子元素表示一個方法。
<?xml version="1.0" encoding="UTF-8"?>
<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 http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
version="2.0">
<description>A tag library exercising SimpleTag handlers.</description>
<tlib-version>1.0</tlib-version>
<short-name>SimpleTagLibrary</short-name>
<uri>mytid</uri>
//定義了一個EL可調用的靜態方法
<function>
<name>sayHello</name> //指明方法名
<function-class>Test_packge.SayHello</function-class> //指明類名
<function-signature>java.long.String sayHello(java.lang.String)</function-signature> //指明方法的返回值,參數情況
</function>
</taglib>
public class SayHello {
public static String sayHello(String s){
return "hello " + s;
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" import="java.util.*" %>
<%@ taglib prefix="fun" uri="mytid" %>
<html>
<head>
<title>index</title>
</head>
<body>
<p>${fun:sayHello("walker")}</p>
</body>
</html>

我們可以看到在EL中調用自定義方法和使用自定義標簽的語法很是類似,之上我們演示了一個EL調用自定義方法的完整流程,並由此可以看出,基本上EL可以調用一切所需資源,可以調用Javabean中方法獲取數據庫中的數據,也可以自定義類實現具體方法供EL調用。功能很是強大。
三、標簽文件(Tag File)
我們之前介紹過了自定標簽的定義和使用,需要三個步驟。首先需要寫一個標簽處理類繼承SimpleTag,然后實現doTag方法就就可以用來處理標簽了,然后我們需要創建一個tld文件,在跟元素taglib中定義一個一個的tag元素,最后在jsp頁面中使用。過程還是很麻煩的。jsp2.0之后,引入了標簽文件,這是一個簡化了的自定義標簽的過程,實際上計算機內部還是將它轉換成了我們自定義的三個步驟,在本節的結尾,我們會一起看看源碼了解下編譯器做了哪些操作。
在Tag File文件中有5個編譯指令,他們是:
- taglib:用於導入其他的標簽庫
- include:用於導入靜態頁面
- tag:類似jsp頁面中的page編譯指令,用於指定頁面的基本屬性
- attribute:用於設定自定義標簽的屬性,值是從jsp頁面傳入的
- variable:可以提供jsp頁面調用的變量
下面定義了一個簡單的Tag file:
//在WEB-INF/tags 文件下創建一個myTag.tag標簽文件
<%@ tag pageEncoding="UTF-8" import="java.util.*"%>
<%@ attribute name="bgColor"%>
<%@ attribute name="cellColor"%>
<%@ attribute name="title"%>
<%@ attribute name="bean"%>
<%@ variable name-given="time" variable-class="java.lang.String" scope="AT_BEGIN" %>
<% jspContext.setAttribute("time","now");%>
<table>
<tr><td>${title}</td></tr>
<%
List<String> list = (List<String>)request.getAttribute("a");
for(Object ele : list){%>
<tr>
<td bgColor="${cellColor}"><%=ele%></td>
</tr>
<%}%>
</table>
<%@ page contentType="text/html;charset=UTF-8" language="java" import="java.util.*" %>
<%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>
<html>
<head>
<title>index</title>
</head>
<body>
<%
List<String> list = new ArrayList<String>();
list.add("walker");
list.add("yam");
request.setAttribute("a",list);
%>
<tags:myTag bgColor="red" cellColor="blue" bean="a" title="測試" />
<p><%=time%></p>
</body>
</html>
我們需要始終知道的是,我們使用所有的標簽,他們返回的結果都是頁面片段,所以如果tag文件頁面全是jsp語法就完全沒有意義了。我們先從index頁面看,前面引入標簽庫不用介紹了,只是這里需要知道我們引入的tagdir並不是這個標簽文件(.tag),而是他的父目錄。(這樣做也是為了頁面簡潔,不用每個標簽文件都引入一次),接着我們看,使用jsp腳本定義了一個list集合,並設置共享范圍。
然后我們使用標簽文件,和使用標簽一樣,這里的bgColor等屬性是對應於我們myTag.tag文件中的attribute編譯指令的,這樣我們這邊傳入的屬性的值就可以自動賦值到標簽文件中了。
接下來我們看看標簽文件,首先使用了tag 編譯指令指定了頁面片段的基本屬性,如編碼方式等。然后就是attribute 編譯指令,用於接受jsp頁面傳入的屬性值。至於這個variable變量的定義,我們先過(后面說)。緊接着是jsp腳本輸出一個表格,不再贅述。我們看看結果:

我們之前說過,標簽文件是簡化了的自定義標簽,最終還是會被編譯成Java類,和我們之前自定義標簽時候寫的標簽處理類差不多。我們打開Tomcat/work....一直找到你的項目的tag文件夾,在里面會發現

我們打開這個標簽文件,看到一坨代碼。我們注意到:
private java.lang.String bgColor;
private java.lang.String cellColor;
private java.lang.String title;
private java.lang.String bean;
public java.lang.String getBgColor() {
return this.bgColor;
}
public void setBgColor(java.lang.String bgColor) {
this.bgColor = bgColor;
jspContext.setAttribute("bgColor", bgColor);
}
....
我們看到這些代碼是我們用編譯指令attribute定義了的變量,經過編譯之后,他們被創建成私有變量並配上set/get方法。這和我們在自定義標簽處理類的時候是一樣的,我們說這樣可以保證jsp頁面傳入的屬性的值可以自動的賦值給這些私有屬性。(web容器干的)。我們還注意到:

這里也是一樣的重寫了doTag方法,同時還看到了標簽文件中內置的對象有:
- request
- response
- session
- application
- config
- out
- jspContext
繞了這么一圈,我們發現所謂的標簽文件本質上還是我們那一套的自定義標簽的流程,只是很大一部分內容交給了編譯器。簡化了我們程序員的工作。
最后我們看看剛才跳過的編譯指令variable,從上面的內容中我們知道,jsp頁面可以通過屬性傳值給我們的標簽文件,但是如果標簽文件想要返回對象給jsp頁面就需要使用到我們的variable編譯指令。這個指令有幾個屬性需要注意下:
- name-given ---指定了該變量的名稱
- variable-clas - ---指定了該變量的數據類型
- scope ---指定該變量作用范圍,這里有三個值
scope的第一個值可以是AT_BEGIN,該值表示此變量可以在標簽體或者向下的代碼中生效,第二值可以是NESTED,該值表示此變量只能出現在標簽體中,第三個可以是AT_END,該值表示此變量只能從標簽結束位置以后的代碼中生效。可以看到第一個值的范圍最大,包含了下面兩個值的范圍。定義完成之后,我們可以使用jspContext.setAttribute("time","now")方法為其賦值,這樣我們的jsp頁面就可以使用該變量了。
本篇文章至此結束,內容很簡單,深入的內容還有待作者繼續學習,希望能有大神指點糾正。
