PGBox
P
G
Box

カスタムタグの作成【基本】

JSPのメニューへ戻る



使用した環境
JDK 6 Update 11
Tomcat 6.0.18

JSPカスタムタグを自作する際のサンプルです。

ボディ部を持つタグとボディ部を持たないタグでは、若干作成方法が異なります。
ボディ部とは、ネストされるタグ要素を示します。
ボディ部なしのタグの例
<sample:abc attr="xxx" />

ボディ部ありのタグの例
<sample:abc attr="xxx">
    body content<br />
</sample:abc>

ボディ部を持たないタグの作成


サンプルとして、パラメータで与えられた値を、String#toUpperCase()を使用して大文字に変換するタグを作成してみます。
まず、カスタムタグの処理を実施するタグハンドルクラスを作成します。
UpperTag.java
package sample;

import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;

/**
 * 大文字に変換を行うタグ
 */
public class UpperTag extends TagSupport {  // (1)
    
    /** 処理対象のオブジェクト */
    private Object value;                   // (2)
    
    @Override
    public int doStartTag() throws JspException {   // (3)
        
        // パラメータがnullの場合は何も行わない
        if (value != null) {
            
            // 大文字に変換を行う。
            String outValue = value.toString().toUpperCase();
            
            // 出力を行う
            JspWriter out = pageContext.getOut();
            try {
                out.write();
            } catch (IOException e) {
                throw new JspException(e);
            }
            
        }
        
        return SKIP_BODY;               // (4)
    }
    
    @Override
    public void release() {
        // クラス変数の開放を行う
        super.release();
        value = null;
    }
    
    /**
     * 処理対象のオブジェクトを設定する
     * @param value
     */
    public void setValue(Object value) {
        this.value = value;
    }
}
(1) javax.servlet.jsp.tagext.TagSupportを継承する事により、このクラスをタグとして実行可能にします。
(2) タグの属性として受け取るパラメータは、クラス変数として定義しsetterメソッドを作成しておきます。
(3) doStartTagメソッドは、開始タグの処理を記述するメソッドです。ボディ部を持たないタグの場合は、このメソッドのオーバーライドだけで実装できます。
(4) 定数「SKIP_BODY」を返すと、ボディ部の処理のスキップを指示できます。

次にtldファイルを作成します。
WEB-INFディレクトリ以下に、以下のようなファイルを作成します。
/WEB-INF/sample.tld
<?xml version="1.0" encoding="UTF-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:xml="http://www.w3.org/XML/1998/namespace"
          xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
          http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
          version="2.1">
              
    <tlib-version>1.0</tlib-version>
    <uri>/sample</uri>
    
    <tag>
        <name>upper</name>
        <tagclass>sample.UpperTag</tagclass>
        <body-content>empty</body-content>
        <attribute>
            <name>value</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
</taglib>

<body-content>empty</body-content>とする事で、ボディ部を処理しないタグである事を定義しています。

tldファイルの詳細な情報についてはtldファイルを参照してください。


以上で自作したタグが使用可能となります。
JSPでは以下のようにして使用できます。
<%@ page contentType="text/html; charset=Windows-31J" pageEncoding="Windows-31J" %>
<%@ taglib uri="/sample" prefix="sample" %>
<%
    String value = "abcdef";
%>
<sample:upper value="<%= value %>" />

このサンプルで出力されるhtmlは以下のようになります。
ABCDEF



ボディ部を持つタグの作成


サンプルとして、ボディ部の値を、String#toUpperCase()を使用して大文字に変換するタグを作成してみます。
まず、カスタムタグの処理を実施するタグハンドルクラスを作成します。
BodyUpperTag.java
package sample;

import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyTagSupport;

/**
 * ボディ部の内容を大文字に変換を行うタグ
 */
public class BodyUpperTag extends BodyTagSupport {  // (1)
    
    @Override
    public int doStartTag() throws JspException {   // (2)
        return EVAL_BODY_BUFFERED;                  // (3)
    }
    
    @Override
    public int doEndTag() throws JspException {     // (4)
        
        // ボディ部の値を取得
        String value = bodyContent.getString();     // (5)
        
        // 大文字に変換
        value = value.toUpperCase();
        
        // 出力を行う
        JspWriter out = pageContext.getOut();
        try {
            out.write(value);
        } catch (IOException e) {
            throw new JspException(e);
        }
        
        return EVAL_PAGE;                           // (6)
    }
    
}
(1) javax.servlet.jsp.tagext.BodyTagSupportを継承する事により、このクラスをボディ部をサポートするタグとして実行可能にします。
(2) doStartTagメソッドは、開始タグの処理を記述するメソッドです。<sample:bodyUpper>部分に相当する処理です。
(3) 定数「EVAL_BODY_BUFFERED」を返すと、ボディ部の内容をバッファリングし、その値を終了タグの処理で使用可能となります。
(4) doEndTagメソッドは、終了タグの処理を記述するメソッドです。</sample:bodyUpper>部分に相当する処理です。
(5) bodyContent.getString()とする事で、ボディ部の内容を取得する事が可能です。
(6) 定数「EVAL_PAGE」を返すと、その後のJSP処理が続行されます。

次にtldファイルを作成します。
WEB-INFディレクトリ以下に、以下のようなファイルを作成します。
/WEB-INF/sample.tld
<?xml version="1.0" encoding="UTF-8"?>
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:xml="http://www.w3.org/XML/1998/namespace"
          xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
          http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
          version="2.1">
          
    <tlib-version>1.0</tlib-version>
    <uri>/sample</uri>
    
    <tag>
        <name>bodyUpper</name>
        <tagclass>sample.BodyUpperTag</tagclass>
        <body-content>JSP</body-content>
    </tag>
</taglib>

<body-content>JSP</body-content>とする事で、ボディ部をJSPとして処理するタグである事を定義しています。

tldファイルの詳細な情報についてはtldファイルを参照してください。


以上で自作したタグが使用可能となります。
JSPでは以下のようにして使用できます。
<%@ page contentType="text/html; charset=Windows-31J" pageEncoding="Windows-31J" %>
<%@ taglib uri="/sample" prefix="sample" %>

<sample:bodyUpper>
    abcdef
</sample:bodyUpper>

このサンプルで出力されるhtmlは以下のようになります。
ABCDEF



タグクラスのメソッドと戻り値

TagSupportやBodyTagSupportがimplementsしているクラス
javax.servlet.jsp.tagext.Tag及びjavax.servlet.jsp.tagext.IterationTagが、カスタムタグの処理を定義しているインターフェイスになります。
実際にはTagSupportやBodyTagSupportを継承しなくてもTagをimplementsし、必要なメソッドを実装すればタグとして動作しますが、
TagSupportやBodyTagSupportを継承する事で実装量を軽減する事ができるようになっています。

タグハンドルのインターフェイスで定義されている各メソッドは以下のようになっています。
メソッドインターフェイス意味
void setPageContext(PageContext pagecontext)TagpageContextオブジェクトを受け取るためのメソッド。
void setParent(Tag tag)Tagネストしている親タグを受け取るためのメソッド。
void setBodyContent(BodyContent bodycontent)BodyTagボディ部の内容を受け取るためのメソッド。
Tag getParent()Tagネストしている親タグを外部へ渡すためのメソッド。
int doStartTag() throws JspExceptionTag開始タグの処理を実装するためのメソッド。処理のタイミングは<xxx:tagattr="xx">もしくは<xxx:tagattr="xx"/>に該当する。
int doEndTag() throws JspExceptionTag終了タグの処理を実装するためのメソッド。処理のタイミングは</xxx:tag>に該当する。
void doInitBody() throws JspExceptionBodyTagボディ部の評価を行う直前に呼び出される。
int doAfterBody() throws JspExceptionIterationTag繰り返し判定を実施するためのメソッド。ボディ部の評価が終わった直後に呼び出される。
void release()Tag変数値などの開放処理を記述する。タグが使用されなくなったタイミングでサーブレットコンテナから呼ばれる。


TagSupportやBodyTagSupportを継承してタグを作成する場合は
setPageContext(), setParent(), getParent(), setBodyContent()メソッドについては既に実装されているので、オーバーライドして実装する必要は特にありません。

doStartTag(), doEndTag(), doAfterBody()には実際の処理を記述する事になりますが、
これらのメソッドは戻すintの値によって、その後の処理が変化します。

メソッドの戻り値に使用するintの定数はTag/IterationTag/BodyTagインターフェイスに定義されています。
定数インターフェイス戻り値に使用した場合の動作使用する場面(メソッド)
SKIP_BODYTagボディ部の評価をスキップする。doStartTag・doAfterBody
EVAL_BODY_INCLUDETagボディ部を評価する。doStartTag
EVAL_BODY_BUFFEREDBodyTagボディ部を評価し、ボディ部の内容をバッファリングしdoAfterBody以降で取得可能にする。doStartTag
EVAL_BODY_AGAINIterationTagボディ部を再度評価する。doAfterBody
SKIP_PAGETagページの残り部分をスキップする。doEndTag
EVAL_PAGETagページの評価を続行する。doEndTag


それぞれのメソッドと定数を組み合わせる事によって、複雑な処理を行うタグを作成する事が可能です。
実践的なサンプルはカスタムタグの作成【分岐・繰り返し】を参照してください。


※ release()メソッドには、xxx = null;といったように、クラス変数の開放処理を記述しておく事となります。
  これは、サーブレットコンテナによっては同じJSPページ内で、タグクラスのインスタンスを使いまわす可能性があるため、
  明確にクリア処理を行っていないと、思わぬバグを引き起こす事となるためです。
  例えばtomcatではタグインスタンスの使いまわしを行います。
  ですが、実際にはrelease()メソッドが呼び出されるタイミングは保証されていないため、
  毎回上書かれない(必須でない)属性値を持つタグの場合はdoEndTag()タグ内で更に
  release()を呼び出すような実装にしておいた方が安全です。






JSPのメニューへ戻る