カスタムタグの基礎 – JSPの拡張機能を自作する

2025-08-04

カスタムタグとは何か

カスタムタグは、JSPの機能を拡張するために開発者が独自に作成できるタグです。JSP標準タグライブラリ(JSTL)で提供されるタグと同じように使用できますが、自分でロジックや表示をカプセル化できる点が特徴です。

カスタムタグの主な特徴:

  • 再利用可能:共通機能をコンポーネント化して複数ページで利用可能
  • 保守性向上:ロジックをタグ内に閉じ込め、JSPから分離
  • 可読性向上:独自の意味的なタグでビジネスロジックを表現
  • 複雑さの隠蔽:内部の複雑な処理をシンプルなタグインターフェースで提供

カスタムタグの基本アーキテクチャ

カスタムタグは以下の主要コンポーネントで構成されます:

  1. タグハンドラクラス:タグの動作を実装するJavaクラス
  2. TLD(Tag Library Descriptor)ファイル:タグライブラリの設定ファイル
  3. JSPページ:カスタムタグを使用するページ

従来のスクリプトレットとの比較

スクリプトレットでの日付フォーマット表示:

<% 
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy年MM月dd日");
String formattedDate = sdf.format(new java.util.Date());
%>
<%= formattedDate %>

カスタムタグを使用した場合:

<my:formatDate pattern="yyyy年MM月dd日" />

カスタムタグの作成手順

1. タグハンドラクラスの作成

javax.servlet.jsp.tagext.SimpleTagSupportを継承したクラスを作成します。

package com.example.tags;

import javax.servlet.jsp.tagext.SimpleTagSupport;
import javax.servlet.jsp.JspException;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class FormatDateTag extends SimpleTagSupport {
    private String pattern;

    public void setPattern(String pattern) {
        this.pattern = pattern;
    }

    @Override
    public void doTag() throws JspException, IOException {
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        String formattedDate = sdf.format(new Date());
        getJspContext().getOut().write(formattedDate);
    }
}

2. TLDファイルの作成

WEB-INF/tags/my.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"
        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>
    <short-name>my</short-name>
    <uri>/WEB-INF/tags/my.tld</uri>

    <tag>
        <name>formatDate</name>
        <tag-class>com.example.tags.FormatDateTag</tag-class>
        <body-content>empty</body-content>
        <attribute>
            <name>pattern</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
</taglib>

3. JSPでの使用

<%@ taglib prefix="my" uri="/WEB-INF/tags/my.tld" %>
<html>
<head>
    <title>カスタムタグデモ</title>
</head>
<body>
    <h1>現在の日付: <my:formatDate pattern="yyyy年MM月dd日" /></h1>
    <p>別の形式: <my:formatDate pattern="MM/dd/yyyy HH:mm:ss" /></p>
</body>
</html>

カスタムタグの種類

1. 空要素タグ(Empty Tag)

<my:currentTime />

2. 属性付きタグ(Tag with Attributes)

<my:repeatText text="Hello" count="5" //>

3. ボディありタグ(Tag with Body)

<my:toUpperCase>
    This text will be converted to uppercase
</my:toUpperCase>

4. ネスト可能タグ(Nested Tags)

<my:table>
    <my:row>
        <my:cell>Data 1</my:cell>
        <my:cell>Data 2</my:cell>
    </my:row>
</my:table>

ボディありタグの実装例

タグハンドラクラス

public class ToUpperCaseTag extends SimpleTagSupport {
    @Override
    public void doTag() throws JspException, IOException {
        StringWriter sw = new StringWriter();
        getJspBody().invoke(sw);  // ボディ内容をStringWriterに出力

        String upperCased = sw.toString().toUpperCase();
        getJspContext().getOut().write(upperCased);
    }
}

TLD定義

<tag>
    <name>toUpperCase</name>
    <tag-class>com.example.tags.ToUpperCaseTag</tag-class>
    <body-content>scriptless</body-content>
</tag>

JSPでの使用

<my:toUpperCase>
    このテキストは大文字に変換されます
</my:toUpperCase>

属性の種類と設定

必須属性

<attribute>
    <name>pattern</name>
    <required>true</required>
</attribute>

オプション属性

<attribute>
    <name>style</name>
    <required>false</required>
</attribute>

動的値を受け付ける属性

<attribute>
    <name>count</name>
    <rtexprvalue>true</rtexprvalue>
</attribute>

JSPでの動的属性値の使用:

<my:repeatText count="${requestScope.repeatCount}" text="Sample" />

カスタムタグのライフサイクル

  1. インスタンス化:タグが初めて使用されるときにタグハンドラが生成
  2. 属性設定:タグの属性値がセッター経由で設定
  3. doTag()呼び出し:タグの処理を実行
  4. 破棄:タグ処理完了後、インスタンスは再利用可能

実践的なカスタムタグ例

ページネーションタグ

public class PaginationTag extends SimpleTagSupport {
    private int currentPage;
    private int totalPages;
    private String url;

    // セッターメソッド省略

    @Override
    public void doTag() throws JspException, IOException {
        JspWriter out = getJspContext().getOut();

        out.print("");
    }
}

JSPでの使用

<my:pagination currentPage="${param.page}" 
               totalPages="${totalPages}" 
               url="products.jsp" />

カスタムタグのベストプラクティス

  1. 単一責任の原則
  • 1つのタグは1つの明確な機能だけを持つようにする
  1. 再利用性を考慮
  • 特定のアプリケーションに依存しない汎用的なタグを作成
  1. パフォーマンスに注意
  • タグハンドラ内で重い処理を行わない
  • 必要に応じてプーリングを検討
  1. 適切なエラーハンドリング
  • 必須属性が不足している場合など、適切なJspExceptionをスロー
  1. ドキュメンテーション
  • タグの使用方法をTLDファイル内に記述
<tag>
    <description>Formats a date according to specified pattern</description>
    <!-- その他の設定 -->
</tag>
  1. テストの実施
  • タグハンドラ単体のテスト
  • JSPページ内での統合テスト

カスタムタグのデバッグ方法

  1. ログ出力
   public void doTag() throws JspException, IOException {
       System.out.println("Debug: pattern = " + pattern);
       // タグ処理
   }
  1. エラーメッセージの詳細化
   if (pattern == null) {
       throw new JspTagException("pattern attribute must be specified");
   }
  1. JSPページでのタグの状態確認
   <c:out value="Debug: ${requestScope.someValue}" />
  1. タグハンドラの単体テスト
   @Test
   public void testFormatDateTag() throws Exception {
       FormatDateTag tag = new FormatDateTag();
       tag.setPattern("yyyy-MM-dd");

       StringWriter sw = new StringWriter();
       JspWriter jspWriter = new MockJspWriter(sw);

       MockJspContext jspContext = new MockJspContext();
       jspContext.setJspWriter(jspWriter);

       tag.setJspContext(jspContext);
       tag.doTag();

       assertEquals("2023-11-15", sw.toString());
   }

カスタムタグの高度なトピック

1. タグファイル(.tagファイル)の使用

JSP-likeな構文でカスタムタグを定義:

WEB-INF/tags/formatDate.tag:

<%@ attribute name="pattern" required="true" rtexprvalue="true" %>
<%@ tag body-content="empty" %>
<%
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat(pattern);
String formattedDate = sdf.format(new java.util.Date());
%>
<%= formattedDate %>

2. ダイナミックアトリビュートの処理

public class DynamicAttributeTag extends SimpleTagSupport 
    implements DynamicAttributes {

    private Map attrs = new HashMap<>();

    @Override
    public void setDynamicAttribute(String uri, String name, Object value) {
        attrs.put(name, value);
    }

    @Override
    public void doTag() throws JspException, IOException {
        // 動的属性を使用した処理
    }
}

3. タグのネストと親子関係

子タグから親タグへのアクセス:

ParentTag parent = (ParentTag)findAncestorWithClass(this, ParentTag.class);
if (parent != null) {
    parent.addItem(item);
}

カスタムタグの実用例

1. セキュリティチェックタグ

<my:hasPermission role="admin">
    <!-- 管理者のみ表示されるコンテンツ -->
</my:hasPermission>

2. データフォーマットタグ

<my:formatCurrency amount="${product.price}" locale="ja_JP" />

3. 条件付きキャッシュタグ

<my:cache key="product_${product.id}" duration="3600">
    <!-- キャッシュされるコンテンツ -->
</my:cache>

4. 国際化タグ

<my:i18n key="welcome.message" />

まとめ

カスタムタグはJSPの強力な拡張機能であり、以下のような利点があります:

  1. 再利用性:共通機能をコンポーネント化してプロジェクト全体で利用可能
  2. 保守性:ロジックをタグ内にカプセル化し、JSPページをシンプルに保つ
  3. 可読性:ドメイン固有のタグでビジネスロジックを直感的に表現
  4. 生産性:複雑な処理をシンプルなタグ呼び出しに抽象化

カスタムタグを適切に設計・実装するためには、以下のポイントを押さえることが重要です:

  • タグの目的と範囲を明確に定義
  • 必要な属性を適切に設計(必須/オプション、動的値対応など)
  • エラーハンドリングを適切に実装
  • パフォーマンスへの影響を考慮
  • 十分なドキュメンテーションを提供

カスタムタグをマスターすることで、JSPベースのWebアプリケーション開発の効率と品質を大幅に向上させることができます。最初はシンプルなタグから始め、徐々に複雑な機能を持つタグを作成していくことをおすすめします。