JavaのServletからModelクラスを呼び出すには?入力値を渡して処理結果を受け取る方法

「ServletからModelを呼び出す」と聞くと、Web専用の難しい仕組みが必要に感じるかもしれません。今回行うのは、別のJavaクラスのメソッドに購入金額を渡し、割引額を戻り値として受け取ることです。
割引の条件をServletに直接書かず、計算を担当するクラスへまとめると、同じルールを別の画面から呼んだり、ブラウザを使わずに確かめたりできます。この記事では、Modelの意味から、実際に値を受け渡すコードまで順に見ていきます。
Schoo「Java入門 中級」第5回「サーバーサイドアプリケーションの実装基礎」に対応しています。未受講の方も、クラス・引数・戻り値の基本を確認しながら読み進められます。
そもそもModelとは?
Modelは、アプリケーションで扱うデータや、計算・判定などの業務上のルールを担当する部分です。購入金額から割引額を決める、在庫が注文数量を満たすか確認する、といった処理が含まれます。こうしたアプリ固有のルールを「ビジネスロジック」と呼びます。
ModelはJavaの予約語や特別な構文ではなく、MVCにおける役割の名前です。今回は、その役割のうち割引計算を担当する普通のJavaクラスを DiscountModel と名付けます。Model という名前を付けたから自動で動くわけではありません。
| よくある疑問 | 今回の考え方 |
|---|---|
| Modelはデータを入れる箱だけ? | いいえ。データだけでなく、割引の判定・計算などもModel側の仕事です。 |
| データベースがないと使わない? | DBを使わない今回の割引計算でも、Modelの役割があります。 |
| クラス名にModelは必須? | 必須ではありません。DiscountCalculatorなど、仕事が分かる名前でも構いません。 |
| Modelは必ず1つのクラス? | いいえ。規模に応じてデータを持つクラス、業務を処理するService、DB操作のDAOなどに分けます。 |
ここではMVC全体を作り直すのではなく、Servletが処理を依頼し、Modelから結果が戻る部分に注目します。
今回作るもの:購入金額から割引額を求める
- 購入金額が10,000円以上なら、割引額は500円。
- 10,000円未満なら、割引額は0円。
- 購入金額は0円以上の整数。負の金額は受け付けない。
- 画面には、購入金額と割引額を表示する。
たとえば12,000円なら500円引きです。戻り値は「割引後の金額11,500円」ではなく、差し引く割引額500円に統一します。まず何を渡し、何を返すメソッドなのかを決めておくと、呼び出す側が迷いません。
| 担当 | 今回すること |
|---|---|
| Servlet | 送信された文字列を受け取り、整数へ変換し、Modelを呼ぶ。結果を表示側へ渡す。 |
| Modelクラス | 整数の購入金額を受け取り、割引額を返す。 |
| JSP | Servletから渡された購入金額と割引額をHTMLに表示する。 |
イラストで見る:引数を渡し、戻り値を受け取る

int amount = 12000;
DiscountModel model = new DiscountModel();
int discount = model.calculateDiscount(amount);
new DiscountModel()で、割引計算を呼び出すためのインスタンスを作る。この時点では割引計算はまだ行わない。model.calculateDiscount(amount)でメソッドを呼ぶ。Servlet側のamountの値12,000が、メソッドの引数へ渡る。- Modelは
amount >= 10000を判定し、return 500;で500を呼び出し元へ返す。 - 呼び出し式の結果500が、
int discount = ...の左側の変数へ代入される。その後、Servletの次の行へ進む。
同じ名前の amount を使っていますが、Servlet側の変数とModel側の引数は別の変数です。int型では値が渡されます。Modelの引数名を変えても、型や引数の順番が合っていれば呼び出せます。
return は画面への表示ではありません。Modelが500を返しても、それだけでブラウザには表示されません。Servletが戻り値を受け取り、表示する処理へ渡す必要があります。
1.Modelクラスに割引のルールを書く
src/main/java/example/DiscountModel.java を作成します。以降はJava 17・Tomcat 10.1の jakarta.servlet を使う構成です。ファイルはUTF-8で保存します。
package example;
public class DiscountModel {
public int calculateDiscount(int amount) {
if (amount < 0) {
throw new IllegalArgumentException(
"購入金額は0円以上にしてください。");
}
if (amount >= 10000) {
return 500;
}
return 0;
}
}
calculateDiscount(int amount) の int amount が受け取る金額です。メソッド名の前の int は、結果を整数で返すことを表します。return 500; が実行されたらメソッドはそこで終了するため、最後の return 0; までは進みません。
このクラスには request も response も登場しません。フォームの入力項目名や、表示するJSPの名前を知らなくても計算できる形です。負の金額を拒否するのは、この処理が受け付ける値のルールなのでModel側で確認します。
2.ServletからModelを呼び出す
src/main/java/example/DiscountServlet.java を作成します。Modelと同じ example パッケージなので、DiscountModelのimportは不要です。別パッケージに置く場合は、そのクラスのimportなどを追加します。
package example;
import java.io.IOException;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet("/discount")
public class DiscountServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
String text = request.getParameter("amount");
if (text == null || text.isBlank()) {
request.setAttribute("error", "購入金額を入力してください。");
} else {
try {
int amount = Integer.parseInt(text.trim());
DiscountModel model = new DiscountModel();
int discount = model.calculateDiscount(amount);
request.setAttribute("amount", amount);
request.setAttribute("discount", discount);
} catch (NumberFormatException e) {
request.setAttribute("error",
"購入金額は0〜2147483647の整数で入力してください。");
} catch (IllegalArgumentException e) {
request.setAttribute("error",
"購入金額は0円以上にしてください。");
}
}
request.getRequestDispatcher("/discount-result.jsp")
.forward(request, response);
}
}
呼び出しの中心は int discount = model.calculateDiscount(amount); です。Servletは割引の条件をもう一度判定せず、Modelが返した割引額を受け取ります。
| コード | 12,000を送ったとき |
|---|---|
request.getParameter("amount") | String型の "12000" を取得する。 |
Integer.parseInt(text.trim()) | int型の 12000 に変換する。 |
model.calculateDiscount(amount) | 12000を渡し、int型の500を受け取る。 |
request.setAttribute("discount", discount) | 結果の500をJSPへ渡せるようにrequestへ保存する。 |
type="number" の入力欄でも、getParameter() で取得する値は文字列です。Modelが必要とするint型に変換してから渡します。
未入力と数値変換の失敗はServletで扱い、負の金額はModelが例外で通知します。NumberFormatException は IllegalArgumentException の子クラスなので、catchは具体的なNumberFormatExceptionを先に書きます。
ここで作るModelの変数や入力値は、doPost内のローカル変数です。Servletのインスタンスは複数のリクエストで使われるため、利用者ごとの入力値をServletのフィールドへ保存しないようにします。
3.入力フォームと結果表示を用意する
src/main/webapp/discount-form.html を作成します。コンテキストパスが /lesson05 なら、http://localhost:8080/lesson05/discount-form.html を開きます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>割引額の確認</title>
</head>
<body>
<h1>割引額の確認</h1>
<form action="discount" method="post"
accept-charset="UTF-8">
<label for="amount">購入金額(円)</label>
<input id="amount" name="amount" type="number"
min="0" max="2147483647" step="1" required>
<button type="submit">割引額を確認する</button>
</form>
</body>
</html>
アプリ直下に配置したこのフォームの送信先は /lesson05/discount です。Servletの @WebServlet("/discount") に対応し、POST送信によってdoPostが動きます。
続いて src/main/webapp/discount-result.jsp を作成します。Servletがrequestに保存した値を取り出して表示します。
<%@ page pageEncoding="UTF-8"
contentType="text/html; charset=UTF-8" %>
<%
String error = (String) request.getAttribute("error");
Integer amount = (Integer) request.getAttribute("amount");
Integer discount = (Integer) request.getAttribute("discount");
%>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>割引額の確認結果</title>
</head>
<body>
<h1>割引額の確認結果</h1>
<% if (error != null) { %>
<p><%= error %></p>
<% } else if (amount != null && discount != null) { %>
<p>購入金額: <%= amount %>円</p>
<p>割引額: <%= discount %>円</p>
<% } else { %>
<p>入力画面から割引額を確認してください。</p>
<% } %>
<a href="discount-form.html">入力画面に戻る</a>
</body>
</html>
Servletの forward() は、サーバー内でJSPへ処理を引き継ぐためのものです。Modelのreturnとは役割が異なります。Modelの戻り値を受け取ったあと、Servletがrequestに値を保存してからJSPへ進みます。
getAttribute() はObject型の値を返すため、ここではStringやIntegerとして取り出します。int型の値をsetAttributeへ渡すとIntegerとして扱われます。JSPは割引の基準を判定せず、計算済みの割引額だけを表示します。
この画面に埋め込んでいるのは、数値とServletで決めた固定メッセージだけです。入力文字列そのものは表示しません。商品名など外部から届く文字列を表示する場合は、HTML用のエスケープが必要です。
フォームから動かして、境界と値の流れを確かめる
| 入力 | 表示される割引額 |
|---|---|
| 0 | 0円 |
| 9999 | 0円 |
| 10000 | 500円 |
| 12000 | 500円 |
10,000円以上という条件なので、9,999と10,000で結果が変わります。アドレスバーから /discount へ直接アクセスするとGETになるため、この例ではフォームを開いて送信してください。結果のJSPを直接開いた場合は、計算値がないので入力画面を案内します。
HTMLのrequiredやminはブラウザ上で入力を助けるものです。それだけに頼らず、フォーム以外からリクエストが来ても、サーバー側で未入力・整数以外・範囲外・負の値を扱えるようにしています。
Modelなら、ブラウザを使わず同じ計算を呼べる
src/main/java/example/DiscountCheck.java を作成し、通常のJavaアプリとしてmainを実行してみます。
package example;
public class DiscountCheck {
public static void main(String[] args) {
DiscountModel model = new DiscountModel();
System.out.println(model.calculateDiscount(9999)); // 0
System.out.println(model.calculateDiscount(10000)); // 500
}
}
コンソールには0と500が順に表示されます。Webサーバー、フォーム、JSPを起動しなくても、割引のルールだけを確認できます。これはModelクラスがHTTPではなく、購入金額という値を受け取る形になっているためです。
別の画面からも同じメソッドを呼べば、割引条件をそれぞれのServletにコピーせずに済みます。JUnitのテストでも、この呼び出しで得た戻り値を期待値と比較できます。ただし、計算だけの確認でフォーム送信や画面表示まで検証できたことにはなりません。
戻り値と画面表示を混同しない
model.calculateDiscount(amount);
request.setAttribute("discount", 0);
このコードでは計算は呼ばれますが、返ってきた割引額を使っていません。画面へ渡す値は自分で指定した0のままです。メソッドを呼ぶことと、戻り値を受け取って使うことは別です。
int discount = model.calculateDiscount(amount);
request.setAttribute("discount", discount);
また、Model内でSystem.out.printlnを使っても、出力されるのはサーバー側のコンソールであり、ブラウザの画面ではありません。計算結果はreturnで返し、表示はJSPへつなぎます。
今回はクラス間の連携を学ぶための金額入力サンプルです。実際の購入処理では、ブラウザから送られた金額をそのまま請求の根拠にせず、サーバー側の商品情報や数量などから金額を確定します。
確認問題
各問題は、本文の元の仕様「10,000円以上なら500円引き」を出発点として、独立して考えてください。
- フォームで12000を送信しました。getParameterで取得する型と値、Modelに渡す型と値、戻り値、Servletのdiscountに入る値を答えてください。
- 条件を「15,000円以上なら800円引き、それ未満は0円」に変更します。DiscountModelのどの部分を変えますか。14,999円と15,000円の戻り値も答えてください。
- 購入金額が12,000円なのに、割引額が0円と表示されました。直前の「戻り値と画面表示を混同しない」にある誤った2行を、戻り値を使う形に修正してください。
- 購入金額が未入力、文字列abc、整数-1だったとき、本文のどの処理が問題を見つけますか。それぞれModelのcalculateDiscountが呼ばれるかも答えてください。
演習後の確認:こちらのページを使って、自分で考えた内容を見直せます。
まとめ
- Modelはデータや業務ルールを担当する役割。今回は普通のJavaクラスに割引のルールをまとめた。
- Servletで文字列を整数に変換し、Modelのメソッドへ引数として渡す。
- Modelは結果をreturnで返し、Servletが変数に受け取ってJSPへ渡す。
- HTTPや表示から計算を分けると、別の画面や通常のJavaコードから同じ処理を呼び出せる。