第4章: ディレクティブとインポート

JSPディレクティブによるページ制御

この章で学ぶこと
  • JSPディレクティブの種類と役割を理解する
  • pageディレクティブによるページ設定の詳細を学ぶ
  • includeディレクティブによるコードの再利用方法を修得する
  • taglibディレクティブとカスタムタグの基本を理解する
  • Javaクラスのインポートと活用方法を学ぶ
  • エラーページの設定と例外処理の実装を理解する

4.1 JSPディレクティブの基本概念

JSPディレクティブは、JSPページの動作や設定を制御するための特殊な命令です。ページ全体の設定、外部ファイルのインクルード、タグライブラリの使用宣言などを行います。

4.1.1 ディレクティブの分類

JSPには3種類の主要なディレクティブがあります:

flowchart TB A["JSPディレクティブ"] --> B["pageディレクティブ"] A --> C["includeディレクティブ"] A --> D["taglibディレクティブ"] B --> B1["ページ設定
(言語、文字エンコーディング)"] B --> B2["インポート
(Javaクラス・パッケージ)"] B --> B3["エラー処理
(エラーページ設定)"] C --> C1["静的インクルード
(翻訳時にファイル結合)"] C --> C2["コードの再利用
(共通部品化)"] D --> D1["カスタムタグ
(JSTLなど)"] D --> D2["タグライブラリ
宣言"]

4.2 pageディレクティブによるページ設定

pageディレクティブは、JSPページの基本的な属性や動作を定義する最も重要なディレクティブです。

4.2.1 主要なpage属性

属性名 用途 デフォルト値
contentType レスポンスのMIMEタイプと文字エンコーディング "text/html; charset=UTF-8" text/html
language スクリプト言語の指定 "java" java
import Javaクラス・パッケージのインポート "java.util.*, java.text.*" -
errorPage エラー発生時の転送先ページ "error.jsp" -
isErrorPage エラーページフラグ "true" false
実習 4-1: pageディレクティブの設定

様々なpageディレクティブ属性を使用して、ページの基本設定を行います。

手順
  1. 以下のJSPファイルをpage-directive-demo.jspとして作成
  2. 各種page属性の効果を確認
  3. エラーページの動作をテスト
page-directive-demo.jsp
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ page language="java" %>
<%@ page import="java.util.*, java.text.*, java.math.*" %>
<%@ page errorPage="error-handler.jsp" %>
<%@ page buffer="8kb" %>
<%@ page autoFlush="true" %>
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>pageディレクティブデモ</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container my-5">
        <h1 class="mb-4">pageディレクティブ機能デモ</h1>
        
        <div class="row">
            <div class="col-md-6">
                <div class="card">
                    <div class="card-header">
                        <h5>インポートしたクラスの使用</h5>
                    </div>
                    <div class="card-body">
                        <%
                            // java.util パッケージのクラス使用
                            Date currentDate = new Date();
                            Calendar calendar = Calendar.getInstance();
                            List<String> dataList = new ArrayList<>();
                            dataList.add("データ1");
                            dataList.add("データ2");
                            dataList.add("データ3");
                            
                            // java.text パッケージのクラス使用  
                            SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 E曜日 HH:mm:ss");
                            DecimalFormat df = new DecimalFormat("#,##0.00");
                            
                            // java.math パッケージのクラス使用
                            BigDecimal bigDecimal = new BigDecimal("123456.789");
                            BigInteger bigInteger = new BigInteger("12345678901234567890");
                        %>
                        
                        <p><strong>現在日時:</strong> <%= sdf.format(currentDate) %></p>
                        <p><strong>年:</strong> <%= calendar.get(Calendar.YEAR) %></p>
                        <p><strong>BigDecimal:</strong> <%= df.format(bigDecimal) %></p>
                        <p><strong>BigInteger:</strong> <%= bigInteger %></p>
                        
                        <p><strong>リストデータ:</strong></p>
                        <ul>
                            <% for (String data : dataList) { %>
                                <li><%= data %></li>
                            <% } %>
                        </ul>
                    </div>
                </div>
            </div>
            
            <div class="col-md-6">
                <div class="card">
                    <div class="card-header">
                        <h5>ページ設定情報</h5>
                    </div>
                    <div class="card-body">
                        <p><strong>コンテントタイプ:</strong> <%= response.getContentType() %></p>
                        <p><strong>文字エンコーディング:</strong> <%= response.getCharacterEncoding() %></p>
                        <p><strong>バッファサイズ:</strong> <%= out.getBufferSize() %> bytes</p>
                        <p><strong>残りバッファ:</strong> <%= out.getRemaining() %> bytes</p>
                        <p><strong>自動フラッシュ:</strong> <%= out.isAutoFlush() ? "有効" : "無効" %></p>
                        
                        <hr>
                        <h6>エラーテスト</h6>
                        <form action="#" method="post">
                            <div class="mb-3">
                                <label for="testNumber" class="form-label">テスト用数値(0で除算エラー):</label>
                                <input type="number" class="form-control" id="testNumber" name="testNumber" value="10">
                            </div>
                            <button type="submit" class="btn btn-warning">エラーテスト実行</button>
                        </form>
                        
                        <%
                            String testNumberStr = request.getParameter("testNumber");
                            if (testNumberStr != null) {
                                int testNumber = Integer.parseInt(testNumberStr);
                                int result = 100 / testNumber; // testNumber=0でArithmeticException
                        %>
                                <div class="alert alert-success mt-3">
                                    計算結果: 100 / <%= testNumber %> = <%= result %>
                                </div>
                        <% } %>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
</html>
error-handler.jsp(エラーページ)
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ page isErrorPage="true" %>
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>エラーが発生しました</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container my-5">
        <div class="alert alert-danger">
            <h4 class="alert-heading">エラーが発生しました</h4>
            <p><strong>エラータイプ:</strong> <%= exception.getClass().getSimpleName() %></p>
            <p><strong>エラーメッセージ:</strong> <%= exception.getMessage() %></p>
            <p><strong>発生時刻:</strong> <%= new java.util.Date() %></p>
            <hr>
            <a href="page-directive-demo.jsp" class="btn btn-primary">元のページに戻る</a>
        </div>
    </div>
</body>
</html>
期待される結果

インポートしたJavaクラスが正常に使用でき、ページ設定情報が表示されます。テスト用数値に0を入力して実行すると、エラーページに転送されることを確認できます。

4.3 includeディレクティブによるコード再利用

includeディレクティブは、他のファイルの内容を現在のJSPページに静的に組み込むために使用されます。ヘッダー、フッター、共通部品などの再利用に有効です。

4.3.1 静的インクルードと動的インクルードの違い

項目 静的インクルード(<%@ include %>) 動的インクルード(<jsp:include>)
処理タイミング 翻訳時(JSP→Servlet変換時) 実行時(リクエスト処理時)
ファイル結合 1つのサーブレットとして結合 別々のサーブレットとして実行
変数スコープ 共有される 独立している
パフォーマンス 高速 やや低速
実習 4-2: includeディレクティブによる部品化

共通部品を別ファイルに分離し、includeディレクティブで再利用する方法を実践します。

手順
  1. 共通ヘッダーファイル(header.jsp)を作成
  2. 共通フッターファイル(footer.jsp)を作成
  3. メインページでincludeディレクティブを使用
  4. コードの再利用効果を確認
header.jsp(共通ヘッダー)
<%@ page contentType="text/html; charset=UTF-8" %>
<%
    String siteName = "テックソリューション";
    String[] menuItems = {"ホーム", "サービス", "会社概要", "お問い合わせ"};
    String currentPage = request.getParameter("page");
    if (currentPage == null) currentPage = "ホーム";
%>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
    <div class="container">
        <a class="navbar-brand" href="#"><%= siteName %></a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarNav">
            <ul class="navbar-nav ms-auto">
                <% for (String menuItem : menuItems) { %>
                    <li class="nav-item">
                        <a class="nav-link <%= menuItem.equals(currentPage) ? "active" : "" %>" 
                           href="?page=<%= menuItem %>"><%= menuItem %></a>
                    </li>
                <% } %>
            </ul>
        </div>
    </div>
</nav>

<div class="bg-light py-3">
    <div class="container">
        <nav aria-label="breadcrumb">
            <ol class="breadcrumb mb-0">
                <li class="breadcrumb-item"><a href="#">ホーム</a></li>
                <% if (!"ホーム".equals(currentPage)) { %>
                    <li class="breadcrumb-item active"><%= currentPage %></li>
                <% } %>
            </ol>
        </nav>
    </div>
</div>
footer.jsp(共通フッター)
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ page import="java.text.SimpleDateFormat" %>
<%
    SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
    String currentYear = yearFormat.format(new java.util.Date());
    String[] socialLinks = {"Twitter", "Facebook", "LinkedIn"};
%>

<footer class="bg-dark text-white mt-5 py-4">
    <div class="container">
        <div class="row">
            <div class="col-md-4">
                <h5>テックソリューション</h5>
                <p>最新のテクノロジーで、あなたのビジネスをサポートします。</p>
            </div>
            <div class="col-md-4">
                <h5>お問い合わせ</h5>
                <p>
                    〒100-0001 東京都千代田区千代田1-1-1<br>
                    TEL: 03-1234-5678<br>
                    Email: info@techsolution.co.jp
                </p>
            </div>
            <div class="col-md-4">
                <h5>SNS</h5>
                <div class="d-flex gap-3">
                    <% for (String social : socialLinks) { %>
                        <a href="#" class="text-white text-decoration-none"><%= social %></a>
                    <% } %>
                </div>
            </div>
        </div>
        <hr>
        <div class="row">
            <div class="col-12 text-center">
                <p class="mb-0">&copy; <%= currentYear %> テックソリューション. All rights reserved.</p>
                <small class="text-muted">最終更新: <%= new java.util.Date() %></small>
            </div>
        </div>
    </div>
</footer>
main-with-includes.jsp(メインページ)
<%@ page contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Include デモページ</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <%-- 共通ヘッダーをインクルード --%>
    <%@ include file="header.jsp" %>
    
    <div class="container my-5">
        <div class="row">
            <div class="col-12">
                <h1>includeディレクティブデモ</h1>
                <p class="lead">この例では、ヘッダーとフッターを別ファイルとして分離し、includeディレクティブで再利用しています。</p>
            </div>
        </div>
        
        <div class="row">
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header">
                        <h5>メインコンテンツ</h5>
                    </div>
                    <div class="card-body">
                        <%
                            // メインページ固有の処理
                            String[] services = {"Webアプリ開発", "データベース設計", "システム保守", "コンサルティング"};
                            java.util.Random rand = new java.util.Random();
                        %>
                        
                        <h6>提供サービス</h6>
                        <div class="row">
                            <% for (String service : services) { %>
                                <div class="col-md-6 mb-3">
                                    <div class="card">
                                        <div class="card-body">
                                            <h6 class="card-title"><%= service %></h6>
                                            <p class="card-text">満足度: <%= rand.nextInt(21) + 80 %>%</p>
                                            <a href="#" class="btn btn-primary btn-sm">詳細を見る</a>
                                        </div>
                                    </div>
                                </div>
                            <% } %>
                        </div>
                    </div>
                </div>
            </div>
            
            <div class="col-md-4">
                <div class="card">
                    <div class="card-header">
                        <h5>お知らせ</h5>
                    </div>
                    <div class="card-body">
                        <ul class="list-unstyled">
                            <li><small class="text-muted">2025-01-15</small><br>
                                新サービス開始のお知らせ</li>
                            <li class="mt-3"><small class="text-muted">2025-01-10</small><br>
                                年末年始休業のお知らせ</li>
                            <li class="mt-3"><small class="text-muted">2025-01-05</small><br>
                                セキュリティアップデート完了</li>
                        </ul>
                    </div>
                </div>
            </div>
        </div>
    </div>
    
    <%-- 共通フッターをインクルード --%>
    <%@ include file="footer.jsp" %>
    
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
期待される結果

ヘッダーとフッターが動的に生成され、メインコンテンツと組み合わせて完全なWebページが表示されます。ナビゲーションメニューのアクティブ状態やパンくずリストも正常に動作します。

理解度確認クイズ
  1. pageディレクティブのcontentType属性の役割と、文字化けを防ぐための正しい設定方法を説明してください。
  2. 静的インクルード(<%@ include %>)と動的インクルード(<jsp:include>)の違いを、処理タイミングとパフォーマンスの観点から説明してください。
  3. エラーページを設定する際のerrorPageとisErrorPage属性の役割と関係性を説明してください。
  4. pageディレクティブのimport属性を使用してJavaクラスをインポートする利点と、複数パッケージを同時にインポートする方法を説明してください。
  5. includeディレクティブを使用してWebページの共通部品化を行う際の設計上の注意点を説明してください。
解答例
  1. contentType属性はレスポンスのMIMEタイプと文字エンコーディングを指定し、"text/html; charset=UTF-8"のように設定することで日本語の文字化けを防ぎます。
  2. 静的インクルードは翻訳時にファイルが結合され1つのサーブレットになるためパフォーマンスが高く、動的インクルードは実行時に別々のサーブレットとして処理されるためやや低速ですが、より柔軟な制御が可能です。
  3. errorPage属性でエラー発生時の転送先を指定し、転送先ページではisErrorPage="true"を設定することでexceptionオブジェクトにアクセスできるようになります。
  4. import属性により完全クラス名を記述せずにクラスを使用でき、"java.util.*, java.text.*"のようにカンマ区切りで複数パッケージを同時にインポートできます。
  5. インクルードされるファイルは独立して動作できるよう設計し、変数名の競合を避け、適切なpage属性設定を行い、依存関係を明確にすることが重要です。

4.4 まとめ

この章では、JSPディレクティブによるページ制御について学習しました。

  • pageディレクティブ:ページ設定、文字エンコーディング、エラー処理の制御
  • includeディレクティブ:静的インクルードによるコード再利用とモジュール化
  • Javaクラスインポート:外部ライブラリの効果的な活用方法
  • エラー処理設定:ユーザーフレンドリーなエラーページの実装
  • コード部品化:保守性を高める共通コンポーネントの設計

次章では、リクエストとレスポンスの処理について詳しく学習し、ユーザー入力の処理とサーバーからの応答制御を修得していきます。