カスタムタグライブラリ
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>
に置き換わるような大域を囲むタグでの利用は一考に値します。