インターネット協会 Java部会
Servlet WG
2001.8.30

実践! JSPタグライブラリ

タグライブラリとは?

JSP1.1 から導入された、カスタムタグ(自分たちで好き勝手に定義できるタグ)を使用するための 仕組み。

使い方は?

JSPの中で、ボディに対する処理を何もしないようなタグ(↓)を使えるようにする場合は...


  <HelloWorld>この部分に対して、何も処理を施さない</HelloWorld>

javax.servlet.jsp.tagext.TagSupport を継承したクラスを作り、TagSupport クラスで定義されて いるメソッドをオーバーライドして処理を実装する。このように、タグで処理する内容を実装した クラスのことを“タグハンドラ”と呼ぶ。 例えば、こんな感じ(↓)の実装をして...

    import java.io.*;
    import javax.servlet.jsp.*;
    import javax.servlet.jsp.tagext.*;


    public class HelloWorldTag extends TagSupport {

        public int doStartTag(){
            return(EVAL_BODY_INCLUDE);
        }

        public int doEndTag(){
            JspWriter out= pageContext.getOut();
            try {
                out.println("Hello, World in taglib.");    
            } catch(IOException e) {
            }
            return(EVAL_PAGE);
        }
    }

TLD(Tag Library Description)ファイルを作って...
    <?xml version="1.0" encoding="ISO-8859-1" ?>
    <!DOCTYPE taglib
        PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
        "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">

        <taglib>

            <tlibversion>1.0</tlibversion>
            <jspversion>1.1</jspversion>
            <shortname>simple</shortname>
            <uri></uri>
            <info>
                A simple tab library for the examples
            </info>

            <tag>
                <name>HelloWorld</name>
                <tagclass>HelloWorldTag>/tagclass>
                <info>Example of HelloWorld.</info>
           </tag>       
       </taglib>

web.xmlにも定義を追加する。
    <taglib>
        <taglib-uri>
            http://www.yellnet.co.jp/taglib/examples
        </taglib-uri>
        <taglib-location>
            /WEB-INF/lib/examples.tld
        </taglib-location>
    </taglib>

以上で設定関連は完了。以下のようなJSPファイル(↓)を用意して、ブラウザで呼び出すと...

    <html>
    <body>
      <%@ taglib uri="http://www.yellnet.co.jp/taglib/examples" prefix="yellnet" %>  

      <yellnet:HelloWorld/>
    </body>
    </html>

JSPの出力は、“この部分に対して、何も処理を施さない Hello, World in taglib.”になる。

タグハンドラとJSPの処理の流れはこんな感じ(↓)。

ボディ対して、何らかの処理を施すようなタグ(↓)を定義する場合は...


  <foo>この部分に対して、タグハンドラで何か処理を行う</foo>

javax.servlet.jsp.tagext.BodyTagSupport を継承したクラスを作り、TagSupport クラスで定義されて いるメソッドをオーバーライドして処理を実装する。処理の流れはこんな感じ(↓)。

タグハンドラのインスタンス化

タグハンドラは、JSPによって以下のようにインスタンス化される。

    HelloWorldTag _jspx_th_yellnet_HelloWorld_0 = new HelloWorldTag();    

この処理(タグハンドラのインスタンス化)はJSPが呼び出されるたびに実行されるので、 効率的にはかなり悪い。JSP1.2 では、タグハンドラのインスタンス化は、JSPの仕事 ではなく、JSP Container の仕事となっている。つまり、EJB と同様の方式である。 これにより、インスタンスをプーリングすることによる、インスタンス化コストの削減 が期待できるだろう。

タグライブラリ採用の経緯

某ECサイト構築プロジェクトにおいて...

上記の理由から、カスタムタグを定義し、デザイナーに動的な部分も含めてページを 作成してもらおうと考えた。

ページ生成用タグ

今回のシステムでは、1ページを論理的に5つの部分に分離するようにした。

上の図の“テンプレート”というのは、ページを構成する各部分を入れるための器のような もので、各部分のレイアウト処理は“テンプレート”で行う。したがって、上の図のように レイアウトするには、<table>タグ等を使ってそのようにレイアウトする必要がある。 例えば、以下のようなテンプレートを作成すると...


          <%@ taglib uri="/tlds/yellnetlib.tld" prefix="yellnet" %>      
                                 :
          <table>
          <tr>
            <td><yellnet:insert template="left"/></td>
            <td><yellnet:insert template="content"/></td>
          </tr>
          </table>
以下のような出力を得ることができる。

leftやcontentの部分に実際にどのような内容が表示されるのかは、 別の画面構成定義ファイル(XML形式)で定義する。


          <screen id="UC1100103">    <---------------- 画面ID。ServletはこのIDを使って
            <template>UC11001template.jsp</template>   JSPを呼び出す。
            <header>left.html</header>
            <content>content.html</content>
          </screen>

特定項目の一覧表示

特定の項目を一覧表示する(ここでの例は、ECサイトの商品一覧表示)ためのタグを 以下のように定義した。


        <%@ taglib uri="/tlds/astylelib.tld" prefix="astyle" %>

        <astyle:iterateItems fallback="URI">  ・・・・・・・・・ </astyle:iterateItems>

fallback属性は、該当する商品が1件もない場合に表示するデータのURIを指定する ようにしている。このタグで囲まれた部分は、商品の個数分だけ繰り返される。各 商品のデータ(商品オブジェクト)は、JSPのJavaBeansにアクセスするためのタグに よってアクセスすることができる。

例)

        <astyle:iterateItems fallback="foo.html">
          商品コード:<jsp:getProperty name="item" value="itemCode"/><br>    
          商品名    :<jsp:getProperty name="item" value="nameKanji"/><p>
        </astyle:iterateItems>
        
        
商品コード:0001 商品名 :XXXX 商品コード:0002 商品名 :YYYY

このタグの実装クラスの一部を以下に示す。

    public class IterateItems extends BodyTagSupport {
                       :
                       :
    public int doStartTag(){
        // Servlet が request または session に商品オブジェクトを格納
        // した List を関連付けておいてくれる。
        List itemCollection = (List)pageContext.findAttribute("ItemList");

        //コレクションが存在する場合
        if (itemCollection_ != null) {
            //コレクションから最初のオブジェクトをリクエストにセットする。
            //商品属性オブジェクト取得
            //targetは、処理対象の商品インデックス
            Item item = (Item)itemCollection.get(target);
            request = (HttpServletRequest)pageContext.getRequest();
            request.setAttribute("item",item);
            
            return(EVAL_BODY_TAG);
        //コレクションがない場合
        } else {
            JspWriter out = pageContext.getOut();
            if (fallback属性が指定されている) {
                fallback属性で指定されたURIの内容を表示
            }

            return SKIP_BODY;
        }
    }

    public int doAfterBody() {
        BodyContent body =getBodyContent();
        JspWriter out = body.getEnclosingWriter();
        String bodyString = body.getString().trim();
        /* 次回このメソッドが呼ばれたとき、
         * 前回までの本体情報蓄積して出力しないようにするため、クリアする。
         */
        body.clearBody();
        //JSPエンジンにより解釈の終わったBodyの出力。
        out.println(bodyString);

        //コレクションに次のオブジェクトがあれば、
        //その1つのオブジェクトを取り出し、リクエストにセットする。
        if (まだ処理すべき商品オブジェクトが存在する) {
            /* 商品属性オブジェクト取得。
             */
            Item item = (Item)itemCollection.get(target);
            request.setAttribute("item",itemEnt);
            target++;

            return EVAL_BODY_TAG;
        } else {
            return SKIP_BODY;
        }
    }

このタグは、Itemオブジェクトを格納したListがセッションに関連付けられていることを 前提として動作する。したがって、検索処理を行うServletにこの処理を行わせている。

しかし、このような仕組みでは、デザイナーの立場としては作業がやりづらいと思われる。 なぜなら、途中でServletを呼び出すような処理が入ってしまう場合、画面遷移の確認もデ ザイナーの作業環境ではできなくなってしまうからである。したがって、以下のような構造にした方が よりよいだろう。

さらに理想を言えば、サーバー側にたくさん部品を用意して、それらにアクセスする ためのカスタムタグを用意し、それらを組み合わせて使うことによって簡単なECサイト ならデザイナーだけで作れてしまうようになるといいな...。

JSP1.2では...

このような繰り返し処理をサポートするためのインタフェース IterationTag が追加されている。 このインタフェースの doAfterBody() メソッドの戻り値が EVAL_BODY_AGAIN だとボディの評価 が再度行われ、SKIP_BODY だとボディの評価が終了するという仕組みになっている...ようです。 実際に試したわけではありません。

文字列置換

特定の文字列を置換するようなタグも用意した。


      <%@ taglib uri="/tlds/astylelib.tld" prefix="astyle" %>

      <astyle:replace old="置換前文字列" new="置換後文字列">
          ・・・・・・・・
      </astyle:replace>

このタグは、タグで囲まれている特定の文字列を置換するためのものである。 データベース上に存在する機種依存文字等を表示する前に置換するような用途のために 用意した。

まとめ


BackIAJ Java部会 Servlet WG
Copyright (C) 1996-2001 インターネット協会 All rights reserved.