javaweb回顧第八篇如何創建自定義標簽

来源:http://www.cnblogs.com/LipeiNet/archive/2016/08/06/5744129.html
-Advertisement-
Play Games

前言:在javaweb開發中自定義標簽的用處還是挺多的。今天和大家一起看自定義標簽是如何實現的。 1:什麼是標簽 標簽是一種XML元素,通過標簽可以使JSP頁面變得簡介易用,而且標簽具有很好的復用性。 2:自定義標簽的標簽庫主要的介面以及類的繼承實現關係圖 3:一步步實現自定義標簽 3.1:Tag接 ...


 前言:在javaweb開發中自定義標簽的用處還是挺多的。今天和大家一起看自定義標簽是如何實現的。

1:什麼是標簽

標簽是一種XML元素,通過標簽可以使JSP頁面變得簡介易用,而且標簽具有很好的復用性。

2:自定義標簽的標簽庫主要的介面以及類的繼承實現關係圖

3:一步步實現自定義標簽

3.1:Tag介面

我們先看一個標簽<td></td>這個標簽有開始標簽和結束標簽,而且還有<tr>這樣的父標簽,那麼實現一個簡單的標簽需要什麼呢

第一:開始標簽  第二:結束標簽第三:資源釋放3個方法,而且還有父標簽,如果我們要得到這個JSP上的內容我們還需要一個PageContext那麼現在我們應該清晰了實現一個標簽需要的元素。ok我們來看看Tag介面都有哪些內容

3.1.1:int doStartTag() throws JspException;這個是開始執行的起始方法

3.1.2:int doEndTag() throws JspException;這個是即將結束的結束方法

3.1.3:void release();釋放對象的資源

3.1.4:void setPageContext(PageContext pc);設置當前頁的上下文對象

3.1.5: void setParent(Tag t);設置父標簽

3.1.6:Tag getParent();獲取父標簽

通過上面的介紹我們現在應該知道怎麼去寫一個標簽了,我們小試牛刀一下

public class HelloTag implements Tag{

    private PageContext pageContext;
    private Tag parent;
    public void setPageContext(PageContext pc) {        
        this.pageContext=pc;//這個方法由jsp頁面的實現對象調用
    }
    public void setParent(Tag t) {    
        this.parent=t;
    }
    public Tag getParent() {

        return parent;
    }
    public int doStartTag() throws JspException {

        return SKIP_BODY;
    }
    public int doEndTag() throws JspException {

        //利用pageContext來獲取jspWriter對象
        JspWriter out=pageContext.getOut();
        try {
            //利用JSPWriter向客戶端輸入信息
            out.print("Hello Tag");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return SKIP_PAGE;
    }
    public void release() {
    }

其中SKIP_BODY表示忽略標簽體內容,下麵我們會說到。既然寫完了一個標簽體我們就開始配置了

首先在WEB-INFO創建一個tlds文件夾然後創建一個tld文件然後設置如下

       <tag>
        <name>hello</name>
        <tag-class>com.lp.tags.HelloTag</tag-class>
        <body-content>empty</body-content>//表示標簽沒有內容
      </tag>

然後我們在創建一個jsp文件然後在jsp文件頭部加上Taglib指令元素<%@ taglib uri="/WEB-INF/tlds/CustomTaglib.tld" prefix="hello"%>

在jsp頁面就可以直接引用HelloTag標簽了比喻我的是<hello:hello></hello:hello>

啟動運行結果如下

有人又問瞭如果<td>這樣的標簽都有屬性啊,如果有屬性我怎麼辦呢,這個也簡單沒有屬性我們就加入屬性。我們來實現一個加法的自定義標簽。這個我使用TagSupport類,從上面圖中我們可以看出這個類實現了Tag介面,它會使我們寫標簽更加簡單

public class AddTag extends TagSupport{
    private int num1;
    private int num2;
    public int getNum2() {
        return num2;
    }
    public void setNum2(int num2) {
        this.num2 = num2;
    }
    public int getNum1() {
        return num1;
    }
    public void setNum1(int num1) {
        this.num1 = num1;
    }
    public int doEndTag() throws JspException
    {
        JspWriter out=pageContext.getOut();
        int num=num1+num2;
        try {
            out.print(num);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return EVAL_PAGE;
    }

有人又問你為什麼沒有寫doStartTag方法啊,其實TagSupport類已經幫我們實現了,它預設情況是忽略標簽中的內容的。現在我們在此配置tld文件

<tag>
        <name>add</name>
        <tag-class>com.lp.tags.AddTag</tag-class>
        <attribute>//表示屬性
            <name>num1</name>屬性命名
            <required>true</required>是否必須輸入
            <rtexprvalue>true</rtexprvalue>是否是可運行的表達式
        </attribute>
        <attribute>
            <name>num2</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>

現在我們在jsp中加入以下代碼

<%@ taglib uri="/WEB-INF/tlds/CustomTaglib.tld" prefix="addtaglib"%>
<body> 自定義的標簽: <% int num1 = Integer.parseInt(request.getParameter("num1")); int num2 = Integer.parseInt(request.getParameter("num2"));%> 演算法: <addtaglib:add num2="<%=num1 %>" num1="<%=num2 %>"></addtaglib:add> </body>

再次運行我們看看結果

有人又說了,你這寫的標簽都沒有標簽內容,你能不能實現一個標簽帶有內容的呢,ok這個沒問題,剛剛我們說了SKIP_BODY表示忽略標簽內容那麼有個相反的EVAL_BODY_INCLUDE表示帶有標簽中的內容,在這裡我們一起實現一個Switch case default三個標簽體聯用的簡單功能

我們先看SwitchTag標簽

public class SwitchTag extends TagSupport{
    private static final long serialVersionUID = 1L;
    //用於判斷子標簽是否已執行
    private boolean childTagExec;
    public SwitchTag()
    {
        childTagExec=false;
    }
    public int doStartTag() throws JspException
    {
        //當遇到switch的起始標簽的時候子標簽還沒執行
        childTagExec=false;
        return EVAL_BODY_INCLUDE;//此時開始執行Switch內部的Case標簽了
    }
    /**
     * 由子標簽處理器對象調用,用於判斷是否可以執行自身的標簽體
     * @return
     */
    public synchronized boolean isExec()
    {
        return (!childTagExec);
    }
    /**
     * 如果子標簽任何一個滿足條件就調用這個方法 通知父標簽
     * 這樣其他子標簽就忽略他們自身標簽體,從而實現Switch case
     */
    public synchronized void childTagSucceeded()
    {
        childTagExec=true;
    }
    public void release()
    {
        childTagExec=false;
    }

CaseTag

public class CaseTag extends TagSupport{
    private static final long serialVersionUID = 1L;
    private boolean cond;//表示條件(比喻case:1此類)
    public CaseTag()
    {
        cond=false;
    }
    public void setCond(boolean cond)
    {
        this.cond=cond;
    }
    public int doStartTag() throws JspException
    {
        Tag parent=getParent();//獲取父標簽
        //判斷是否可以執行自身標簽
        if(!((SwitchTag)parent).isExec())
        {
            return SKIP_BODY;
        }
        //如果條件為true,則通知父標簽有一個子標簽滿足條件
        //否則忽略標簽體
        if(cond)
        {
            ((SwitchTag)parent).childTagSucceeded();
            return EVAL_BODY_INCLUDE;
        }
        else {
            return SKIP_BODY;
        }
    }
    public void release()
    {
        cond=false;
    }

 DefaultTag

public class DefaultTag extends TagSupport{
    private static final long serialVersionUID = 1L;
    public int doStartTag() throws JspException
    {
        Tag parent=getParent();
        if (!((SwitchTag)parent).isExec()) {
            return SKIP_BODY;
        }
        ((SwitchTag)parent).childTagSucceeded();//如果所有Case都不滿足則執行Default標簽
        return EVAL_BODY_INCLUDE;
    }
}

我們在次配置tld文件

<tag>
        <name>switch</name>
        <tag-class>com.lp.tags.SwitchTag</tag-class>
        <body-content>jsp</body-content>
    </tag>
    <tag>
        <name>case</name>
        <tag-class>com.lp.tags.CaseTag</tag-class>
        <body-content>jsp</body-content>
        <attribute>
            <name>cond</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
    <tag>
        <name>default</name>
        <tag-class>com.lp.tags.DefaultTag</tag-class>
        <body-content>jsp</body-content>
    </tag>

其中<body-content>jsp</body-content>中jsp表示支持jsp具有的一切功能(比喻jsp9種內置對象)

<body>
    <%
        String userName = request.getParameter("userName");
    %>
    <mytag:switch>
        <mytag:case cond='<%=userName.equals("zhangsan")%>'>
            <%out.print("張三");%>
        </mytag:case>
        <mytag:case cond='<%=userName.equals("lisi")%>'>
            <%out.print("李四");%>
        </mytag:case>
        <mytag:case cond='<%=userName.equals("wangwu")%>'>
            <%out.print("王五");%>
        </mytag:case>
        <mytag:default>
            <%out.print("無");%>
        </mytag:default>
    </mytag:switch>
</body>

現在開始執行效果如下

3.1:IterationTag介面

上面我們都一直說的標簽內容都一次性完成,但是如果是迴圈標題體內容怎麼辦,那麼就用到了IterationTag介面,此介面增加了一個方法

public int doAfterBody() throws JspException該方法表示每次對標簽體處理之後被調用也就是說在doStartTag方法之後doEndTag方法之前被調用,如果沒有的話就不執行。

新增加了一個常量EVAL_BODY_AGAIN表示再次執行標簽體。現在我們實現一個獲取多條用戶信息展示的功能

public class UserBean implements Serializable{

    private static final long serialVersionUID = 1L;

    public UserBean(){}

    public UserBean(String userName,int age,String email)
    {
        this.age=age;
        this.email=email;
        this.userName=userName;
    }
    private String userName;
    private int age;
    private String email;
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
}
public class IterateTag extends TagSupport{
    private static final long serialVersionUID = 1L;
    
    private Iterator items;//獲取集合
    private String itemId;//對象的標識
    private Object item;//迭代對象中的每一個對象
    public IterateTag()
    {
        items=null;
    }
    public void release()
    {
        items=null;
    }
    /**
     * 得到集合的迭代對象
     */
    public void setItems(Collection cl)
    {
        if(cl.size()>0)
            items=cl.iterator();
    }
    public void setVar(String var)
    {
        this.itemId=var;
    }
    public int doStartTag()throws JspException
    {
        if(items.hasNext())//首先被執行
        {
            item=items.next();
        }
        else{
            return SKIP_BODY;
        }
        saveItems();//把迭代的對象保存在pageContext中
        return EVAL_BODY_INCLUDE;
    }
    public int doAfterBody() throws JspException
    {
        if(items.hasNext())//直到把迭代對象中的每一項都放進pageContext中
        {
            item=items.next();
        }
        else{
            return SKIP_BODY;
        }
        saveItems();
        return EVAL_BODY_AGAIN;
    }
    public void saveItems()
    {
        if(item==null)
        {
            pageContext.removeAttribute(itemId,pageContext.PAGE_SCOPE);
        }
        else{
            pageContext.setAttribute(itemId, item);//如果加入相同的id會進行覆蓋
        }
    }
}
<tag>
        <name>iterate</name>
        <tag-class>com.lp.tags.IterateTag</tag-class>
        <body-content>JSP</body-content>
        <attribute>
            <name>items</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
            <name>var</name>
            <required>true</required>
            <rtexprvalue>false</rtexprvalue>
        </attribute>
    </tag>
<body>
    <%
        ArrayList al = new ArrayList();
        UserBean user1 = new UserBean("zhangsan", 25, "[email protected]");
        UserBean user2 = new UserBean("lisi", 15, "[email protected]");
        UserBean user3 = new UserBean("wangwu", 35, "[email protected]");
        al.add(user1);
        al.add(user2);
        al.add(user3);
    %>
    <table>
        <tr>
            <td>用戶名</td>
            <td>年齡</td>
            <td>郵箱</td>
        </tr>
        <iterator:iterate items="<%=al%>" var="user">
        <jsp:useBean id="user" class="com.lp.beans.UserBean"></jsp:useBean>
            <tr>        
                <td><jsp:getProperty property="userName" name="user" /></td>
                <td>${user.age}</td>
                <td>${user["email"]}</td>
            </tr>
        </iterator:iterate>
    </table>
</body>

效果如下

 

4:簡單標簽開發

為了簡化自定義標簽開發,JSP2.0加入了簡單標簽的開發實現的介面是SimpleTag,我們一起看下SimpleTag的主要方法

4.1: public void setJspContext( JspContext pc )該方法被容器調用,設置JspContext,JspContext 是PageContext的基類

4.2:public void setParent( JspTag parent );設置父標簽

4.3:public JspTag getParent();獲取父標簽

4.4:public void setJspBody( JspFragment jspBody );該方法用於設置標簽體標簽體,標簽體由JspFragment對象提供,可以把JspFragment看做是一個對象封裝一段JSP代碼,可以被多次執行。

4.5:public void doTag(),主要處理標簽和標簽體的業務邏輯

public class WelcomeSimpleTag extends SimpleTagSupport{
  private JspFragment jspFragment;
  private String name;
  public void setJspBody(JspFragment jspBody)
  {
      this.jspFragment=jspBody;
  }
  public void setName(String name)
  {
      this.name=name;
  }
  public void doTag() throws JspException, IOException
  {
      JspContext jspContext=getJspContext();
      JspWriter out=jspContext.getOut();
      out.print(name);
      jspFragment.invoke(null);
  }

然後在jsp頁面之間調用即可。關於簡單標簽的開發,大家可以自行實踐。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 用nopcomerce3.8版本的同行註意了,前2天發佈3.8正式版後,作者收到一些BuG,作者修複後重新提供了一個源代碼包下載地址,不是github上的那個鏈接。去作者官網論壇我那個鏈接地址,或關註微信公眾號aspnetcore發消息獲取鏈接地址。我們只能幫你到這了 原文地址:http://www ...
  • MVVM學習筆記 1、MVVM的簡介 MVVM模式是Model-View-ViewModel模式的簡稱,也就是由模型(Model)、視圖(View)、視圖模型(ViewModel),其目的是為了實現將業務和界面分開,降低耦合度。 2、示例(綁定TextBox和Combox控制項) 總體結構: View ...
  • quartznet 上篇說到quartznet這個東東,topshelf+quartznet有很多不錯的文章,可以查看七七同學的文章(http://www.cnblogs.com/jys509/p/4628926.html)。這裡我主要說說cron表達式,如果玩過linux下定時任務的肯定不陌生。 ...
  • 最近在項目中碰到一個很頭疼的問題,在前端連接事件中寫了一個廣播線程,該廣播線程寫在while迴圈中,但是前臺會有很多個客戶端,沒連接一次就會有一個廣播線程開啟,很吃資源,剛開始我解決這個問題的方法是每次觸發連接事件是檢測一下當前連接數,如果是count_client<=1,就開線程,否則跳過廣播,但 ...
  • 在開發過程中經常需要發佈到開發環境、測試環境或者預發佈環境上給其他同事進行測試驗證效果等等,每次發佈都要備份,拷貝,修改配置文件等等重覆操作非常的麻煩,效率大打折扣,而web部署提供了這樣的解決方案:在服務端安裝Web Deploy服務,由Web Deploy服務完成備份發佈等操作,今天小編就以圖文 ...
  • 一、前言 Treeview控制項常用於遍歷本地文件信息,通常與Datagridview與ImageList搭配。ImageList控制項用於提供小圖片給TreeView控制項,DatagridView通常顯示TreeNode節點下文件及文件夾的信息。 效果圖: 二、代碼 初始化窗體: 初始化DataGri ...
  • 項目架構採用:Asp.Net MVC4.0 + EntityFramework6.0 code first + AutoMapper + Unity(IOC) + SqlServer2012 項目地址:www.xiaoboke.net 這個博客會不斷開發完善哦 歡迎大家去吐槽和提建議,一起學習,一起 ...
  • 使用自動載入和解析url的參數,實現調用到不同的控制器,實現了pathinfo模式和普通的url模式 文件結構: |--Controller |--Index |--Index.php |--Application.php Application.php \Controller\Index\Inde ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...