?????本篇文章介紹自定義標(biāo)簽,可能在工作中很少涉及到自己來(lái)定義一個(gè)標(biāo)簽庫(kù)铣卡,因?yàn)槲覀兓旧隙际鞘褂玫拇笊駥?xiě)的標(biāo)簽庫(kù)锉桑,基本上直接使用即可排霉,但是從自身的發(fā)展來(lái)看,通往高級(jí)程序員的道路上民轴,開(kāi)發(fā)框架就需要大量的使用到標(biāo)簽庫(kù)技術(shù)攻柠。本文將從以下幾個(gè)方面介紹自定義標(biāo)簽庫(kù)的基本知識(shí)點(diǎn):
- 背景以及作用
- 開(kāi)發(fā)簡(jiǎn)單的標(biāo)簽
- 開(kāi)發(fā)帶屬性的標(biāo)簽
- 開(kāi)發(fā)帶標(biāo)簽體的標(biāo)簽
- 以頁(yè)面片段為屬性的標(biāo)簽
- 具有動(dòng)態(tài)屬性的標(biāo)簽
一、標(biāo)簽庫(kù)有什么作用
?????自定義標(biāo)簽庫(kù)是一種優(yōu)秀的表現(xiàn)層技術(shù)杉武,之前介紹的MVC模式,我們使用jsp作為表現(xiàn)層辙售,但是jsp語(yǔ)法嵌套在html頁(yè)面轻抱,美工還是很難直接參與開(kāi)發(fā),并且jsp腳本和html代碼耦合在一起旦部,維護(hù)成本較高祈搜。我們能不能開(kāi)發(fā)一套和html風(fēng)格類(lèi)似并且能完成jsp腳本功能的標(biāo)簽來(lái)解決這種低效的協(xié)作方式呢较店?于是標(biāo)簽庫(kù)就誕生了。
這是Java中標(biāo)簽規(guī)范的繼承體系容燕,實(shí)現(xiàn)Tag接口的我們叫做傳統(tǒng)式標(biāo)簽庫(kù)開(kāi)發(fā)梁呈,這種開(kāi)發(fā)模式略顯發(fā)復(fù)雜,基本已經(jīng)被SimpleTag式的簡(jiǎn)單式開(kāi)發(fā)標(biāo)簽庫(kù)給取代了蘸秘。Java中提供了一個(gè)默認(rèn)的實(shí)現(xiàn)類(lèi)SimpleTagSupport來(lái)實(shí)現(xiàn)自定義標(biāo)簽官卡,我們只要繼承此類(lèi)即可。
二醋虏、開(kāi)發(fā)一個(gè)最簡(jiǎn)單的標(biāo)簽庫(kù)
?????開(kāi)發(fā)一個(gè)自定義標(biāo)簽庫(kù)的過(guò)程如下:
- 開(kāi)發(fā)自定義標(biāo)簽處理類(lèi)
- 創(chuàng)建*.tld文件寻咒,每個(gè)此文件對(duì)應(yīng)一個(gè)標(biāo)簽庫(kù),標(biāo)簽庫(kù)中可以由多個(gè)標(biāo)簽
- 在jsp頁(yè)面使用標(biāo)簽
首先我們先從自定義標(biāo)簽處理類(lèi)開(kāi)始颈嚼,正如上文所說(shuō)毛秘,這個(gè)類(lèi)只有繼承了SimpleTagSupport這個(gè)類(lèi)可以省去省去重寫(xiě)SimpleTag接口中的一些方法。我們說(shuō)個(gè)doTag()這個(gè)方法很重要阻课,這個(gè)方法類(lèi)似于我么main方法一樣叫挟,當(dāng)jsp頁(yè)面加載到我們定義的標(biāo)簽的時(shí)候就會(huì)過(guò)來(lái)調(diào)用這個(gè)方法。
public class MyTag extends SimpleTagSupport {
@Override
public void doTag() throws JspException, IOException {
getJspContext().getOut().write("hello walker");
}
}
這是一個(gè)簡(jiǎn)單的標(biāo)簽處理類(lèi)限煞,具體的細(xì)節(jié)暫時(shí)不用關(guān)心抹恳,只需要知道,它負(fù)責(zé)向jsp頁(yè)面輸出字符串即可晰骑。下面我們看看第二步适秩,創(chuàng)建*tld文件。這個(gè)文件我們沒(méi)有必要重新寫(xiě)一遍硕舆,到Tomcat服務(wù)器上的webapps/examples/WEB-INF/jsp2中復(fù)制一個(gè)過(guò)來(lái)秽荞,修改名字存放到我們的項(xiàng)目中WEB-INF的任意子路徑下。刪除一些標(biāo)簽成如下內(nèi)容:
我們看到這是一個(gè)XML文件抚官,根元素為taglib扬跋,而taglib主要有以下幾個(gè)子元素:
- description //描述信息
- tlib-version //指定標(biāo)簽庫(kù)的版本號(hào),基本不用我們操心
- short-name //指定標(biāo)簽庫(kù)的短名字凌节,也是沒(méi)什么用
- uri //這是一個(gè)重要的子元素钦听,是該標(biāo)簽庫(kù)的唯一標(biāo)識(shí)
- tag //看名字就知道,這是定義標(biāo)簽的子元素倍奢,很重要
?????對(duì)于taglib這個(gè)根元素朴上,我們主要關(guān)心他下面的uri和tag兩個(gè)子元素,一個(gè)標(biāo)簽庫(kù)可以由多個(gè)標(biāo)簽卒煞,也就是可以有多個(gè)tag標(biāo)簽痪宰。關(guān)于tag標(biāo)簽,主要有以下幾個(gè)子元素:
- description //描述信息
- name //該標(biāo)簽的唯一標(biāo)識(shí),很重要
- tag-class //指定了處理該標(biāo)簽的類(lèi)衣撬,也就是使用該標(biāo)簽誰(shuí)給我返回結(jié)果
- body-content //標(biāo)簽體乖订,后面詳說(shuō),很重要
- attribute //屬性具练,后面介紹乍构,很重要
對(duì)于以上標(biāo)簽大家可能已經(jīng)知道什么意思,但是具體用在什么地方可能不清楚扛点,本小節(jié)的最后會(huì)綜合三個(gè)步驟自定義一個(gè)簡(jiǎn)單的標(biāo)簽哥遮。接下來(lái)介紹在jsp頁(yè)面是如何使用標(biāo)簽。
?????使用標(biāo)簽庫(kù)也是有兩個(gè)步驟占键,首先導(dǎo)入標(biāo)簽庫(kù)昔善,然后引用標(biāo)簽。我們使用taglib編譯指令導(dǎo)入標(biāo)簽庫(kù)畔乙,具體格式如下:
<%@ taglib uri="tld文件中指定的唯一標(biāo)識(shí)" prefix="指定標(biāo)簽前綴"%>
我們看到這個(gè)導(dǎo)入標(biāo)簽庫(kù)的編譯指令主要有兩個(gè)屬性君仆,一個(gè)是用于定位我們已經(jīng)寫(xiě)好的標(biāo)簽庫(kù),定位的方法就是讀取每個(gè)tld文件中的URI元素的值牲距,prefix用于指定我們使用標(biāo)簽時(shí)的前綴返咱,等用的時(shí)候就很容易理解了,現(xiàn)在解釋反而不容易說(shuō)清楚牍鞠。我們使用標(biāo)簽的格式如下:
<剛剛指定的前綴 :標(biāo)簽名 />
標(biāo)簽名就是我們標(biāo)簽庫(kù)中每個(gè)tag都會(huì)有的name的值咖摹,這指定了該語(yǔ)句是引用的那個(gè)標(biāo)簽。下面我們通過(guò)具體的例子直觀(guān)感受下难述。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib uri="mytid" prefix="mytags"%>
<html>
<head>
<title></title>
</head>
<body>
<mytags:hello />
</body>
</html>
<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>
<tag>
<description>Outputs a colored tile</description>
<name>hello</name>
<tag-class>Test.MyTag</tag-class>
<body-content>empty</body-content>
</tag>
</taglib>
public class MyTag extends SimpleTagSupport {
@Override
public void doTag() throws JspException, IOException {
getJspContext().getOut().write("hello walker");
}
}
結(jié)果如下:
?????這就完成了一個(gè)最簡(jiǎn)單的標(biāo)簽庫(kù)的定義和使用的過(guò)程萤晴,首先我們?cè)趇ndex.jsp頁(yè)面通過(guò)URI引入mytag標(biāo)簽庫(kù),指定了使用該標(biāo)簽庫(kù)的前綴為mytags胁后,然后<mytags:hello />引用name為hello的tag標(biāo)簽店读,然而在加載這條語(yǔ)句的時(shí)候會(huì)通過(guò)我們的tag中指定的處理類(lèi),找到它并執(zhí)行攀芯,最后通過(guò)此標(biāo)簽處理類(lèi)想我們的jsp頁(yè)面輸出了一個(gè)字符串屯断。以上就是一個(gè)最簡(jiǎn)單的自定義標(biāo)簽的過(guò)程,為了更好的理解后續(xù)的較復(fù)雜的自定義標(biāo)簽方式侣诺,上述內(nèi)容值得感受體會(huì)殖演。
三、開(kāi)發(fā)帶屬性的標(biāo)簽
?????假如我們通過(guò)攔截器獲取了從數(shù)據(jù)庫(kù)查出來(lái)的一個(gè)結(jié)果集年鸳,我們此處希望調(diào)用標(biāo)簽來(lái)將結(jié)果集以表格的形式輸出來(lái)趴久,此時(shí)我們的這個(gè)結(jié)果集又該如何傳到標(biāo)簽處理類(lèi)中呢?這時(shí)我們可以使用屬性搔确。具體看代碼:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib uri="mytid" prefix="mytag"%>
<html>
<head>
<title></title>
</head>
<body>
<%
HashMap<String,Integer> maps = new HashMap<String, Integer>();
maps.put("李四",53);
maps.put("張三",23);
maps.put("walker",22);
pageContext.setAttribute("map",maps);
%>
<table>
<mytag:hello map="map"/>
</table>
</body>
</html>
<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>
<tag>
<description>Outputs a colored tile</description>
<name>hello</name>
<tag-class>Test.MyTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>map</name>
<required>true</required>
<fragment>true</fragment>
</attribute>
</tag>
</taglib>
public class MyTag extends SimpleTagSupport {
private String map;
public String getMap(){
return this.map;
}
public void setMap(String map){
this.map = map;
}
@Override
public void doTag() throws JspException, IOException {
HashMap<String,Integer> maps = (HashMap<String,Integer>)(getJspContext().getAttribute(map));
Object[] array = maps.keySet().toArray();
for (String str : maps.keySet()){
getJspContext().getOut().write("<tr>");
getJspContext().getOut().write("<td>");
getJspContext().getOut().write(str);
getJspContext().getOut().write("</td>");
getJspContext().getOut().write("<td>");
getJspContext().getOut().write(""+maps.get(str));
getJspContext().getOut().write("</td>");
getJspContext().getOut().write("</tr>");
}
}
}
我們首先先從index.jsp頁(yè)面看起彼棍,首先我們定義了一個(gè)HashMap用來(lái)存放一個(gè)簡(jiǎn)單的個(gè)人信息已添,鍵為姓名值為年齡。最后我們?cè)O(shè)置共享范圍為當(dāng)前page滥酥。然后<mytag:hello map="map"/>,這里的map=“map”畦幢,第一個(gè)map是屬性名坎吻,第二個(gè)只是一個(gè)字符串。下面進(jìn)入到tld頁(yè)面看宇葱,這個(gè)頁(yè)面基本沒(méi)有什么改動(dòng)瘦真,只是多了個(gè)attribute元素,attribute中有幾個(gè)子元素黍瞧,第一個(gè)是name指定這個(gè)屬性的唯一標(biāo)識(shí)诸尽,第二個(gè)required指定該屬性是否是必須屬性。第三個(gè)fragment指定該屬性是否支持jsp腳本印颤。主要關(guān)心name這個(gè)元素您机。這個(gè)值和jsp頁(yè)面調(diào)用標(biāo)簽時(shí)使用的屬性名必須一樣,并且這個(gè)屬性值還必須和標(biāo)簽處理類(lèi)中的私有屬性名一樣年局,這就是為了jsp頁(yè)面的屬性值能夠自動(dòng)的傳入到標(biāo)簽處理類(lèi)的屬性中际看,我們看這個(gè)標(biāo)簽處理類(lèi)
?????這個(gè)類(lèi)定義了私有屬性map,和我們的tld文件中的屬性名是一致的矢否。getJspContext().getAttribute(map)仲闽,首先是獲得了調(diào)用該標(biāo)簽的jsp頁(yè)面的pageContext,這就是方法getJspContext的返回值僵朗,因?yàn)槲覀冊(cè)趈sp頁(yè)面設(shè)置了一個(gè)共享數(shù)據(jù)(maps)赖欣,于是我們同名名字獲取該對(duì)象,這里的map就是我們的私有屬性验庙,他的值被自動(dòng)賦值了顶吮,具體的值就是jsp頁(yè)面?zhèn)魅氲膮?shù)。后面的代碼就很簡(jiǎn)單了壶谒,循環(huán)輸出數(shù)據(jù)到j(luò)sp頁(yè)面上云矫。
?????稍微理一下思路,這種帶屬性的標(biāo)簽使用其實(shí)和無(wú)屬性差不多汗菜,都是先引入了標(biāo)簽庫(kù)让禀,加載標(biāo)簽的的時(shí)候通過(guò)URI找到對(duì)應(yīng)的標(biāo)簽庫(kù),只不過(guò)這次將一個(gè)字符串賦值給了tld中attribute元素中名為map的屬性陨界,然后跳向?qū)?yīng)的標(biāo)簽處理類(lèi)巡揍,順便把map屬性的值自動(dòng)賦值處理類(lèi)中的私有屬性,然后執(zhí)行輸出代碼菌瘪。其中需要注意的是屬性名一定要統(tǒng)一腮敌,另外阱当,如果標(biāo)簽的屬性值是8種基本數(shù)據(jù)類(lèi)型,那么在JSP頁(yè)面在傳遞字符串時(shí)糜工,JSP引擎會(huì)自動(dòng)轉(zhuǎn)換成相應(yīng)的類(lèi)型弊添,但如果標(biāo)簽的屬性值是復(fù)合數(shù)據(jù)類(lèi)型,那么JSP引擎是無(wú)法自動(dòng)轉(zhuǎn)換的捌木。對(duì)于傳遞非基本數(shù)據(jù)類(lèi)型的操作油坝,后續(xù)文章會(huì)介紹。
?????為了篇幅不過(guò)于長(zhǎng)刨裆,還剩下的內(nèi)容留在下篇澈圈,如有錯(cuò)誤,望指出帆啃!