カスタムタグライブラリ
JSP ではプログラマがカスタムタグライブラリ (Custom Tag Library; 拡張タグライブラリ) を作成する事で <jsp:include> のような機能を自由に定義することができます。
カスタムタグは Java コード挿入による可視性低下を防ぐのに大変有用ですが、ページ固有の処理までカスタムタグ化するのは却って手間と管理が膨大になり大変です。一般的には以下のような局面での効果が期待できます。
- 定型記述の簡略化
-
共通のページヘッダ、ページフッタ、スタイルシートや JavaScript の取り込み、キャッシュ制御命令といった記述をカスタムタグ化しておくことで JSP が簡素になり後々のメンテナンスも楽になります。
- 汎用機能の部品化
-
タブ切り替えやツリー表示などの汎用的な機能を部品化したり、二度押し防止、バックスペース無効化、自動入力値チェックなどを追加したコンポーネントを作成することができます。
- フレームワークとの統合
-
フレームワークが管理するリソースやアプリケーションデータへの JSP からのアクセス手段として使用することができます。
JSTL や JSF
などでは汎用的なカスタムタグの多くを既に実装していますので、実際に作成する前にこれらの組み合わせで解決できないか調べてみる事をお勧めします。
呼び出し構造
カスタムタグは XML に似た接頭辞 (名前空間) を付けた記述を行うことで示されます。
JSP プロセッサはその JSP で宣言されている <%@taglib%> ディレクティブに基づいて接頭辞に対応するTLD ファイル (Tag Library Definition
File) を参照し、どのタグハンドラ(Tag Handler) を呼び出せばよいかを決定します。
TLD とは「どのタグ名 (ローカル名) が使用された時にどのタグハンドラを呼び出すか」を定義したファイルです。クラス名の他に、タグがどういった属性を取り、どういった内容を持つかなどを定義しています。
タグハンドラ
カスタムタグライブラリは Tag インターフェースを実装した タグハンドラ (Tag
Handler) と呼ばれるクラスが処理を行います。拡張タグを作成するときに使用するスーパークラス/インターフェースは以下の特徴があります。
| インターフェース |
Tag |
IterationTag |
BodyTag |
SimpleTag |
| 開始位置での処理 |
○ |
○ |
○ |
○ |
| 終了位置での処理 |
○ |
○ |
○ |
○ |
| タグ内部のスキップ |
○ |
○ |
○ |
○ |
| ページ評価の終了 |
○ |
○ |
○ |
○ |
| タグ内部の繰り返し評価 |
× |
○ |
○ |
○ |
| タグ内部評価結果の取得 |
× |
× |
○ |
○ |
| フラグメントによる実装の簡略化 |
× |
× |
× |
○ |
| タグ内部でのスクリプトレット使用 |
○ |
○ |
○ |
× |
どのインターフェースを実装するかによって可能なことが違ってきます。また
SimpleTag と他のタグハンドラは設計が大きく異なります。これから覚えるのであれば安全で扱いやすい SimpleTag をお勧めします。
それぞれのインターフェースに対して定型メソッドを実装した
XxxTagSupport クラスが提供されています。大抵は上記のインターフェースから実装するよりこれらのクラスのサブクラスを作成したほうが楽です。
実装ガイダンス
- インスタンスの同期化は不要
-
タグハンドラのインスタンスがマルチスレッドで実行されることはありません。これはインスタンスごとに設定される PageContext 自体がリクエスト間で共有できるオブジェクトでないことからも分かります。
ただし static 宣言したクラス変数は従来のクラスと同様に複数のスレッドからの同時アクセスが行われますので注意して下さい。
- doEndTag(), release() の呼び出しを期待しない
-
タグの開始やタグ内部の処理で例外が発生した場合 doEndTag(), release()
は呼び出されません。リソースの開放などの確実なクリーンアップ処理が必要なタグハンドラは
TryCatchFinally
を実装してください。
SimpleTag は 1 メソッド内でタグの処理が完結するため、通常の Java コードと同じように try-finally を利用したリソース開放が可能です。
- インスタンスプールによる再利用に注意
-
最近のサーブレットコンテナのほとんどはインスタンス構築によるパフォーマンス劣化を防ぐ目的で、処理の終わったタグハンドラを次回以降の処理でも再利用しています。このため、タグの処理で使用する内部状態 (インスタンス変数) を持つタグハンドラは必ず release() メソッドをオーバーライドして初期状態に戻す必要があります。
プールされているインスタンスは setParent() などのいくつかの初期化メソッドが省略される可能性があります。同様に、属性を静的に指定した (つまりスクリプトレットや EL を使用していない) タグハンドラは setter の再呼び出しが行われることなく処理が開始される可能性があります。
SimpleTag はフールプルーフを意識しておりインスタンスがキャッシュされない事が保障されています。実装に自信がないのであればこちらを使用してください。
- バッファリングの使用に注意
-
BodyTag のバッファリング機能を使用するとタグの内容を文字列として取得できますが、これはタグ内の出力がいったん全てメモリに保管されるため乱用は禁物です。またバッファリングを行っている間は flush() が行えません。
Tag ハンドラ
IterationTag ハンドラ
BodyTag ハンドラ
SimpleTag ハンドラ
SimpleTag
、
SimpleTagSupport
は JSP 2.0 で導入された、従来のタグハンドラより使いやすく安全な設計に変更されたタグハンドラです。
SimpleTag は返値による煩わしいフロー制御を行う必要がありません。
doTag() メソッド一つをオーバーライドし、この中の好きな位置でタグ内を評価することができます。もちろん if や for で内容をスキップしたり繰り返したり、また評価結果を文字列として取得する事も可能です。
しかしこの動作は JSP 2.0 で導入された
JspFragment
という機能を利用して実現しているため、フラグメント内、つまり SimpleTag
で実装したタグ内では スクリプトレットを記述することができません (EL やカスタムタグは利用可能です; またタグ外であればスクリプトレットも利用可能です)。
最近ではスクリプトレットをなるべく使わない風潮ではありますが、任意の場所で使えないという制約となってしまうと少々不便です。製品やオープンソースのようにどう使われるか想定できない場合や、<html>, <body>
に置き換わるような大域を囲むタグでの利用は一考に値します。