第3章: スクリプトレットと式
JSPスクリプト要素の詳細理解
この章で学ぶこと
- スクリプトレット(<% %>)の詳細な使用方法と適用場面を理解する
- 式(<%= %>)による動的コンテンツ出力の効果的な活用法を学ぶ
- 宣言(<%! %>)によるクラスレベル要素の定義方法を修得する
- JSPページ内でのJavaコード記述のベストプラクティスを理解する
- 制御構造(条件分岐・繰り返し)をJSPで実装する方法を学ぶ
- 動的コンテンツ生成の実践的な応用例を通じて理解を深める
3.1 スクリプトレット(<% %>)の基本理解
スクリプトレットは、JSPページ内でJavaコードを直接記述するための最も基本的な構文要素です。条件分岐、繰り返し処理、変数操作など、様々なプログラムロジックを実装できます。
3.1.1 スクリプトレットの特徴と用途
スクリプトレットの主な特徴:
- Javaコード実行:標準的なJava構文をそのまま記述可能
- 変数スコープ:メソッドレベルのローカル変数として機能
- 制御構造:if文、for文、while文など全ての制御構造が使用可能
- オブジェクト操作:Javaオブジェクトの生成・操作が自由に実行可能
実習 3-1: スクリプトレットの基本操作
各種データ型の変数宣言と基本的な操作を行うJSPページを作成します。
手順
- 以下のJSPファイルをscriptlet-basics.jspとして作成
- 各種データ型の変数宣言と操作を確認
- ブラウザで実行して結果を観察
scriptlet-basics.jsp
<%@ page contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>スクリプトレット基本操作</title>
<style>
body { font-family: 'Noto Sans JP', sans-serif; margin: 20px; }
.result { background: #f8f9fa; padding: 10px; margin: 10px 0; border-left: 4px solid #0d6efd; }
</style>
</head>
<body>
<h1>スクリプトレット基本操作デモ</h1>
<%
// 基本データ型の変数宣言
int integerValue = 42;
double doubleValue = 3.14159;
boolean booleanValue = true;
String stringValue = "Hello, JSP!";
// 配列の宣言と初期化
int[] numbers = {1, 2, 3, 4, 5};
String[] colors = {"赤", "青", "緑", "黄", "紫"};
// 日付と時刻の取得
java.util.Date currentDate = new java.util.Date();
java.util.Calendar cal = java.util.Calendar.getInstance();
// 数学的計算
double circleArea = Math.PI * Math.pow(5, 2);
int randomNumber = (int)(Math.random() * 100) + 1;
%>
<h2>基本変数の表示</h2>
<div class="result">
<p>整数値: <%= integerValue %></p>
<p>浮動小数点数: <%= doubleValue %></p>
<p>論理値: <%= booleanValue %></p>
<p>文字列: <%= stringValue %></p>
<p>現在日時: <%= currentDate %></p>
<p>円の面積(半径5): <%= String.format("%.2f", circleArea) %></p>
<p>ランダム数値: <%= randomNumber %></p>
</div>
<h2>配列要素の表示</h2>
<div class="result">
<p>数値配列:</p>
<ul>
<% for (int i = 0; i < numbers.length; i++) { %>
<li>numbers[<%= i %>] = <%= numbers[i] %></li>
<% } %>
</ul>
<p>色名配列:</p>
<ul>
<% for (String color : colors) { %>
<li><%= color %></li>
<% } %>
</ul>
</div>
<h2>条件分岐の例</h2>
<div class="result">
<% if (randomNumber < 30) { %>
<p style="color: red;">低い値です(<%= randomNumber %>)</p>
<% } else if (randomNumber < 70) { %>
<p style="color: orange;">中程度の値です(<%= randomNumber %>)</p>
<% } else { %>
<p style="color: green;">高い値です(<%= randomNumber %>)</p>
<% } %>
</div>
</body>
</html>
期待される結果
様々なデータ型の変数が表示され、配列の繰り返し処理、条件分岐による動的な表示が確認できます。ページを更新するたびにランダム値と条件分岐の結果が変化します。
3.2 式(<%= %>)による動的出力
式は、Javaの式や変数の値を直接HTML出力に埋め込むための構文です。スクリプトレットで処理したデータを表示する際に最も頻繁に使用されます。
3.2.1 式の活用パターン
式の一般的な使用パターン:
実習 3-2: 式を活用した動的コンテンツ生成
様々な式の活用パターンを使用して、実用的な動的コンテンツを生成します。
手順
- 以下のJSPファイルをexpression-demo.jspとして作成
- 各種式の使用方法を確認
- 動的に生成される内容の変化を観察
expression-demo.jsp
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ page import="java.text.*, java.util.*" %>
<!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>
<%
// データ準備
String companyName = "テックソリューション株式会社";
String[] departments = {"営業部", "開発部", "総務部", "人事部"};
double[] sales = {1250.5, 2100.8, 890.3, 670.2};
// フォーマッター設定
DecimalFormat currencyFormat = new DecimalFormat("#,##0.0");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日(E)");
// 集計計算
double totalSales = 0;
for (double sale : sales) {
totalSales += sale;
}
// 現在時刻情報
Calendar now = Calendar.getInstance();
int hour = now.get(Calendar.HOUR_OF_DAY);
String timeGreeting = (hour < 12) ? "おはようございます" : (hour < 18) ? "こんにちは" : "こんばんは";
%>
<div class="container my-5">
<div class="row">
<div class="col-12">
<div class="alert alert-primary" role="alert">
<h4 class="alert-heading"><%= timeGreeting %>!</h4>
<p class="mb-0"><%= companyName %>の売上レポート(<%= dateFormat.format(new Date()) %>)</p>
</div>
</div>
</div>
<div class="row">
<div class="col-md-8">
<div class="card">
<div class="card-header">
<h5>部門別売上実績</h5>
</div>
<div class="card-body">
<table class="table table-striped">
<thead>
<tr>
<th>部門名</th>
<th class="text-end">売上金額(万円)</th>
<th class="text-end">構成比(%)</th>
<th>評価</th>
</tr>
</thead>
<tbody>
<% for (int i = 0; i < departments.length; i++) { %>
<tr>
<td><%= departments[i] %></td>
<td class="text-end"><%= currencyFormat.format(sales[i]) %></td>
<td class="text-end"><%= String.format("%.1f", (sales[i] / totalSales) * 100) %></td>
<td>
<% if (sales[i] >= 1500) { %>
<span class="badge bg-success">優秀</span>
<% } else if (sales[i] >= 1000) { %>
<span class="badge bg-warning">普通</span>
<% } else { %>
<span class="badge bg-danger">要改善</span>
<% } %>
</td>
</tr>
<% } %>
</tbody>
<tfoot>
<tr class="table-dark">
<th>合計</th>
<th class="text-end"><%= currencyFormat.format(totalSales) %></th>
<th class="text-end">100.0</th>
<th>-</th>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card mb-3">
<div class="card-header">
<h5>サマリー情報</h5>
</div>
<div class="card-body">
<h6>総売上金額</h6>
<p class="h3 text-primary"><%= currencyFormat.format(totalSales) %>万円</p>
<h6>平均売上</h6>
<p class="h4 text-info"><%= currencyFormat.format(totalSales / departments.length) %>万円</p>
<h6>最高売上部門</h6>
<%
int maxIndex = 0;
for (int i = 1; i < sales.length; i++) {
if (sales[i] > sales[maxIndex]) {
maxIndex = i;
}
}
%>
<p class="h5 text-success"><%= departments[maxIndex] %></p>
</div>
</div>
<div class="card">
<div class="card-header">
<h5>システム情報</h5>
</div>
<div class="card-body">
<small class="text-muted">
レポート生成時刻: <%= new SimpleDateFormat("HH:mm:ss").format(new Date()) %><br>
処理時間: <%= System.currentTimeMillis() % 1000 %>ms<br>
Javaバージョン: <%= System.getProperty("java.version") %>
</small>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
期待される結果
部門別売上レポートが動的に生成され、時刻に応じた挨拶、フォーマットされた数値、計算結果、条件に応じたバッジ表示などが確認できます。
3.3 宣言(<%! %>)によるクラスレベル要素
宣言は、JSPページのクラスレベルでメソッドや変数を定義するための構文です。スクリプトレットや式から呼び出せる共通的な機能を実装する際に使用します。
3.3.1 宣言とスクリプトレットの違い
宣言とスクリプトレットの主な違い:
項目 | 宣言(<%! %>) | スクリプトレット(<% %>) |
---|---|---|
スコープ | クラスレベル(全リクエスト共通) | メソッドレベル(リクエスト毎) |
使用目的 | メソッド・インスタンス変数定義 | 処理ロジック実装 |
生存期間 | サーブレットの生存期間中 | リクエスト処理中のみ |
初期化 | クラスロード時に1回 | リクエスト毎に実行 |
実習 3-3: 宣言を使用したユーティリティ機能実装
宣言を活用して再利用可能なメソッドを作成し、スクリプトレットから呼び出します。
手順
- 以下のJSPファイルをdeclaration-demo.jspとして作成
- 宣言で定義したメソッドの呼び出しを確認
- アクセス毎にカウンターが増加することを確認
declaration-demo.jsp
<%@ page contentType="text/html; charset=UTF-8" %>
<%!
// クラスレベルの変数宣言(全リクエストで共有)
private int visitCounter = 0;
private java.util.Date serverStartTime = new java.util.Date();
// ユーティリティメソッドの宣言
public String formatCurrency(double amount) {
java.text.DecimalFormat df = new java.text.DecimalFormat("#,##0円");
return df.format(amount);
}
public String getGradeByScore(int score) {
if (score >= 90) return "A";
if (score >= 80) return "B";
if (score >= 70) return "C";
if (score >= 60) return "D";
return "F";
}
public String getBadgeClass(String grade) {
switch(grade) {
case "A": return "bg-success";
case "B": return "bg-primary";
case "C": return "bg-warning";
case "D": return "bg-secondary";
default: return "bg-danger";
}
}
public boolean isPrime(int number) {
if (number < 2) return false;
for (int i = 2; i <= Math.sqrt(number); i++) {
if (number % i == 0) return false;
}
return true;
}
// アクセス記録用メソッド
public synchronized int incrementVisitCounter() {
return ++visitCounter;
}
%>
<%
// 現在のアクセス番号を取得
int currentVisit = incrementVisitCounter();
// サンプルデータ
String[] studentNames = {"田中太郎", "佐藤花子", "鈴木次郎", "高橋美咲", "渡辺健太"};
int[] scores = {95, 82, 76, 68, 45};
double[] prices = {1980.0, 5280.0, 12800.0, 980.0, 25600.0};
// サーバー稼働時間計算
long uptimeMillis = System.currentTimeMillis() - serverStartTime.getTime();
long uptimeMinutes = uptimeMillis / (1000 * 60);
%>
<!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="row">
<div class="col-12">
<div class="alert alert-info">
<h4>サーバー情報</h4>
<p>あなたは<strong><%= currentVisit %></strong>番目の訪問者です</p>
<p>サーバー開始時刻: <%= serverStartTime %></p>
<p>稼働時間: <%= uptimeMinutes %>分</p>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5>成績管理システム</h5>
</div>
<div class="card-body">
<table class="table">
<thead>
<tr>
<th>学生名</th>
<th>点数</th>
<th>評価</th>
</tr>
</thead>
<tbody>
<% for (int i = 0; i < studentNames.length; i++) { %>
<%
String grade = getGradeByScore(scores[i]);
String badgeClass = getBadgeClass(grade);
%>
<tr>
<td><%= studentNames[i] %></td>
<td><%= scores[i] %>点</td>
<td><span class="badge <%= badgeClass %>"><%= grade %></span></td>
</tr>
<% } %>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5>商品価格一覧</h5>
</div>
<div class="card-body">
<table class="table">
<thead>
<tr>
<th>商品ID</th>
<th>価格</th>
<th>税込価格</th>
</tr>
</thead>
<tbody>
<% for (int i = 0; i < prices.length; i++) { %>
<tr>
<td>PROD-<%= String.format("%03d", i + 1) %></td>
<td><%= formatCurrency(prices[i]) %></td>
<td><%= formatCurrency(prices[i] * 1.10) %></td>
</tr>
<% } %>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5>数学関数デモ</h5>
</div>
<div class="card-body">
<p>1から20までの数値における素数判定:</p>
<div>
<% for (int i = 1; i <= 20; i++) { %>
<% if (isPrime(i)) { %>
<span class="badge bg-success me-1"><%= i %></span>
<% } else { %>
<span class="badge bg-light text-dark me-1"><%= i %></span>
<% } %>
<% } %>
</div>
<small class="text-muted">緑色のバッジが素数です</small>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
期待される結果
訪問者カウンター、成績評価、価格フォーマット、素数判定など、宣言で定義したメソッドが各機能で活用され、アクセスするたびにカウンターが増加することが確認できます。
3.4 制御構造を活用した動的コンテンツ生成
JSPでは、Javaの全ての制御構造(条件分岐・繰り返し処理)を使用できます。これらを効果的に活用することで、複雑な動的コンテンツを生成できます。
3.4.1 主要な制御構造パターン
JSPでよく使用される制御構造:
実習 3-4: 制御構造を活用した実用的なWebページ
複雑な制御構造を組み合わせて、実用的なWebアプリケーションページを作成します。
手順
- 以下のJSPファイルをcontrol-structures.jspとして作成
- 各種制御構造の動作を確認
- 動的に生成される様々なパターンを観察
control-structures.jsp
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ page import="java.util.*, java.text.*" %>
<%!
// 天気情報を取得する模擬メソッド
public String getWeatherInfo(String city) {
String[] weathers = {"晴れ", "曇り", "雨", "雪"};
Random rand = new Random();
return weathers[rand.nextInt(weathers.length)];
}
// 温度に基づく服装アドバイス
public String getClothingAdvice(int temperature) {
if (temperature >= 25) return "半袖・薄着がおすすめです";
if (temperature >= 20) return "軽い上着があると良いでしょう";
if (temperature >= 15) return "長袖・軽いジャケットを";
if (temperature >= 10) return "コートが必要です";
return "厚手のコート・防寒具を";
}
%>
<%
// サンプルデータの生成
String[] cities = {"東京", "大阪", "名古屋", "福岡", "札幌"};
int[] temperatures = {22, 18, 25, 28, 5};
String[] prefectures = {"東京都", "大阪府", "愛知県", "福岡県", "北海道"};
// 現在時刻に基づく処理
Calendar cal = Calendar.getInstance();
int currentHour = cal.get(Calendar.HOUR_OF_DAY);
String timeOfDay = (currentHour < 6) ? "深夜" :
(currentHour < 12) ? "午前" :
(currentHour < 18) ? "午後" : "夜";
// 統計計算
double avgTemp = 0;
int maxTemp = Integer.MIN_VALUE;
int minTemp = Integer.MAX_VALUE;
String hottestCity = "", coldestCity = "";
for (int i = 0; i < temperatures.length; i++) {
avgTemp += temperatures[i];
if (temperatures[i] > maxTemp) {
maxTemp = temperatures[i];
hottestCity = cities[i];
}
if (temperatures[i] < minTemp) {
minTemp = temperatures[i];
coldestCity = cities[i];
}
}
avgTemp /= temperatures.length;
%>
<!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">
<style>
.weather-card { transition: all 0.3s ease; }
.weather-card:hover { transform: translateY(-5px); }
</style>
</head>
<body>
<div class="container my-5">
<div class="row">
<div class="col-12 text-center">
<h1 class="mb-4">全国天気情報システム</h1>
<div class="alert alert-primary">
<strong><%= timeOfDay %></strong>の天気情報をお届けします
(<%= new SimpleDateFormat("yyyy年MM月dd日 HH:mm").format(new Date()) %>現在)
</div>
</div>
</div>
<div class="row">
<% for (int i = 0; i < cities.length; i++) { %>
<div class="col-md-4 mb-4">
<div class="card weather-card h-100">
<div class="card-header text-center
<% if (temperatures[i] >= 25) { %>bg-danger text-white
<% } else if (temperatures[i] >= 15) { %>bg-warning
<% } else { %>bg-info text-white<% } %>">
<h5 class="mb-0"><%= cities[i] %>(<%= prefectures[i] %>)</h5>
</div>
<div class="card-body text-center">
<h2 class="card-title
<% if (temperatures[i] >= 25) { %>text-danger
<% } else if (temperatures[i] <= 5) { %>text-primary
<% } else { %>text-success<% } %>">
<%= temperatures[i] %>°C
</h2>
<p class="card-text">
<strong>天気:</strong> <%= getWeatherInfo(cities[i]) %><br>
<strong>服装:</strong> <%= getClothingAdvice(temperatures[i]) %>
</p>
<% // 特別な注意事項 %>
<% if (temperatures[i] >= 30) { %>
<div class="alert alert-danger alert-sm">
<strong>熱中症注意!</strong> 水分補給を忘れずに
</div>
<% } else if (temperatures[i] <= 0) { %>
<div class="alert alert-warning alert-sm">
<strong>凍結注意!</strong> 路面状況にご注意ください
</div>
<% } %>
</div>
<div class="card-footer text-muted text-center">
<small>観測地点: <%= i + 1 %>番</small>
</div>
</div>
</div>
<% } %>
</div>
<div class="row mt-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5>統計情報</h5>
</div>
<div class="card-body">
<div class="row text-center">
<div class="col-md-3">
<h6>平均気温</h6>
<p class="h4 text-primary"><%= String.format("%.1f", avgTemp) %>°C</p>
</div>
<div class="col-md-3">
<h6>最高気温</h6>
<p class="h4 text-danger"><%= maxTemp %>°C</p>
<small class="text-muted">(<%= hottestCity %>)</small>
</div>
<div class="col-md-3">
<h6>最低気温</h6>
<p class="h4 text-info"><%= minTemp %>°C</p>
<small class="text-muted">(<%= coldestCity %>)</small>
</div>
<div class="col-md-3">
<h6>観測地点数</h6>
<p class="h4 text-success"><%= cities.length %>地点</p>
</div>
</div>
<hr>
<h6>温度帯別地点数</h6>
<div class="row">
<%
int hot = 0, warm = 0, cool = 0, cold = 0;
for (int temp : temperatures) {
if (temp >= 25) hot++;
else if (temp >= 15) warm++;
else if (temp >= 5) cool++;
else cold++;
}
%>
<div class="col-3 text-center">
<span class="badge bg-danger">暑い(25°C以上)</span>
<p class="h5 mt-2"><%= hot %>地点</p>
</div>
<div class="col-3 text-center">
<span class="badge bg-warning">温暖(15-24°C)</span>
<p class="h5 mt-2"><%= warm %>地点</p>
</div>
<div class="col-3 text-center">
<span class="badge bg-info">涼しい(5-14°C)</span>
<p class="h5 mt-2"><%= cool %>地点</p>
</div>
<div class="col-3 text-center">
<span class="badge bg-primary">寒い(5°C未満)</span>
<p class="h5 mt-2"><%= cold %>地点</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
期待される結果
全国の天気情報システムが表示され、温度に応じた色分け、条件に応じた警告メッセージ、統計計算結果などが動的に生成されます。リロードするたびに天気情報が変化します。
3.5 スクリプト要素使用のベストプラクティス
保守性が高く、読みやすいJSPコードを記述するための重要な原則を学習します。
避けるべき書き方
- 長大なスクリプトレット:100行を超えるようなJavaコードをJSP内に記述
- 複雑なビジネスロジック:JSP内でデータベース処理や複雑な計算を実行
- HTMLとJavaの混在:可読性を著しく損なう構造
- エラーハンドリングの欠如:例外処理を考慮しないコード
3.5.1 推奨される開発パターン
- 関心の分離:プレゼンテーション層とビジネスロジック層の明確な分離
- 短いスクリプトブロック:1つのスクリプトレットは10行程度に抑制
- 意味のある変数名:コードの可読性向上
- 適切なコメント:複雑な処理には日本語コメントを追加
- エラーハンドリング:例外発生に対する適切な対応
理解度確認クイズ
- スクリプトレット、式、宣言の使い分けについて、それぞれの特徴と適用場面を具体例とともに説明してください。
- 宣言(<%! %>)で定義した変数が「全リクエストで共有される」ことの意味と、それによって生じる可能性のある問題について説明してください。
- JSP内でfor文を使用して動的にHTMLテーブルを生成する際の基本的な記述パターンを説明してください。
- JSPのスクリプト要素を使用する際に「関心の分離」が重要な理由を、保守性の観点から説明してください。
- 式(<%= %>)でnullの可能性がある変数を出力する際に考慮すべき点と対策方法を説明してください。
解答例
- スクリプトレット(<% %>)は変数宣言や制御構造などの処理ロジックに使用、式(<%= %>)は値をHTML出力に直接埋め込む際に使用、宣言(<%! %>)はクラスレベルのメソッドやインスタンス変数を定義する際に使用します。
- 宣言で定義した変数はサーブレットのインスタンス変数となり、異なるユーザーリクエスト間で共有されるため、マルチスレッド環境でのデータ競合や意図しない値の変更が発生する可能性があります。
- <% for (データ型 変数 : コレクション) { %> <tr><td><%= 変数 %></td></tr> <% } %> のように、繰り返し処理の開始と終了でHTMLタグを適切に配置します。
- 関心の分離により、デザイン(HTML)、データ処理(Java)、スタイリング(CSS)を独立して管理でき、各専門分野での保守・拡張が容易になり、チーム開発の効率性が向上します。
- NullPointerExceptionを避けるため、三項演算子(<%= value != null ? value : "デフォルト値" %>)や事前のnullチェックを行い、安全にデータを出力する必要があります。
3.6 まとめ
この章では、JSPの核となるスクリプト要素について詳しく学習しました。
- スクリプトレット活用:処理ロジックと制御構造の実装方法
- 式による動的出力:データを効果的にHTML出力に埋め込む技法
- 宣言の効果的利用:再利用可能なメソッドとクラスレベル要素の定義
- 制御構造の組み合わせ:複雑な動的コンテンツ生成の実践
- 開発ベストプラクティス:保守性の高いJSPコードの記述原則
次章では、JSPのディレクティブとインポート機能について学習し、より構造化されたJSPアプリケーション開発方法を修得していきます。