結論から言うと、Javaサーブレットは、SpringもJakarta EEも内部で使い続けている「Web開発の心臓部」です。
「今でも学ぶ意味があるの?」と思うかもしれませんが、ここを押さえると後続のフレームワーク学習が一気に速くなります。
私もサーブレットでつまずいた経験があるからこそ、初心者がどこで詰まるかを把握しています。
最新のJakarta EE 11/Tomcat 11系に対応し、仕組み・サンプルコード・Tomcatでの動かし方・セキュリティ対策を7ステップにまとめました。
読み終えれば、以下の悩みが解決します。
この記事でわかること
- Javaサーブレットの基本的な役割と仕組みがわかる
- 簡単なWebアプリケーションを自分で作れるようになる
- 現代的なフレームワークを学ぶための強固な土台を築ける
Web開発の第一歩を、ここから一緒に踏み出しましょう。
PARTS LIST /目次
Javaサーブレットとは?
Javaサーブレットは、Webアプリの根幹をなす重要な技術で、多くのフレームワークも内部でこの仕組みを利用しています。まずはサーブレットがどのようなもので何をしているのかを理解していきましょう。
サーブレットの役割と仕組み
Javaサーブレットとは、ひとことで言うと「サーバー側で動くJavaプログラム」のことです。
Webブラウザからの要求(リクエスト)を受け取り、それに応じた処理を実行します。
そして、結果として動的なWebページ(HTMLなど)を生成し、ブラウザに応答(レスポンス)を返す役割を担います。
この一連の流れは、以下のようになります。
- リクエスト: ユーザーがWebブラウザでURLにアクセスします。
- 受信: Webサーバーがそのリクエストを受け取ります。
- 処理依頼: Webサーバーは、リクエストに対応するJavaサーブレットを呼び出します。
- ビジネスロジック実行: サーブレットは、データベースへのアクセスや計算などの処理を実行します。
- レスポンス生成: 処理結果をもとに、動的にHTMLを生成します。
- 応答: 生成したHTMLをWebサーバー経由でブラウザに返します。
Javaサーブレットはユーザーの見えないサーバー側で働き、Webページを動的に作り出す心臓部なのです。
JSPとの違い・MVCモデルでの役割分担
Javaサーブレットとよく一緒に登場するのが、JSP(JavaServer Pages)です。
両者は役割が異なりますが、協力してWebアプリケーションを構築します。
- Javaサーブレット: ロジックを記述するのが得意です。データベース連携や複雑な計算などを担当します。
- JSP: HTMLの中にJavaのコードを埋め込む形で、見た目を作るのが得意です。画面の表示デザインを担当します。
サーブレットのJavaコード内にHTMLをたくさん書くのは大変です。逆に、JSPに複雑なロジックを書くと見通しが悪くなります。
そのため、処理はJavaサーブレット、表示はJSPというように役割分担するのが一般的です。
これをMVC(Model-View-Controller)モデルと呼び、効率的な開発を可能にします。
サーブレットコンテナ(Tomcat)とは
作成したJavaサーブレットは、単体では動作しません。
サーブレットを動かすためには、「サーブレットコンテナ」または「Webコンテナ」と呼ばれる特別なソフトウェアが必要です。
その代表例が、Apache Tomcatです。Tomcatは、サーブレットコンテナとして以下の重要な役割を果たします。
2026年版・パッケージ名の重要な変更
サーブレットAPIを含むJava EEは、2017年にOracleがEclipse Foundationへの移管を発表し「Jakarta EE」として引き継がれ、Jakarta EE 9(2020年リリース)以降はパッケージ名が javax.servlet.* から jakarta.servlet.* に変更されています。
Tomcat 10系以降は jakarta 名前空間のみサポートし、javax のままのコードはClassNotFoundExceptionで動きません。
2025年6月にはJakarta EE 11がGAし、Tomcat 11.0系(Servlet 6.1対応)がこれに対応した安定版です。
新規学習では Tomcat 11.0系 + jakarta.servlet を選んでください(Tomcat 10.1系も引き続きサポートされているため、既存教材や環境の都合で10.1系を使う場合も基本的な書き方は同じです)。
なお、次期版のJakarta EE 12はJava SE 21ベースで策定が進められています(2026年9月時点では未リリース)。
- サーブレットのライフサイクル管理: サーブレットの生成から破棄までを管理します。
- リクエストとレスポンスの管理: ブラウザからのリクエストを解析し、対応するサーブレットに渡します。また、サーブレットが生成したレスポンスをブラウザに返します。
- 通信の管理: HTTPプロトコルに基づいた通信を処理します。
簡単に言えば、Tomcatは「Javaサーブレットが快適に働くための実行環境」です。
私たちはTomcatのようなコンテナに自作のサーブレットを配置(デプロイ)することで、Webアプリケーションを公開できるのです。
体験談:駆け出し時にTomcatでハマった話
私が初めてTomcatを触った時、Eclipse上でサーバーを起動しても「Address already in use」のエラーが解消できず、丸一日悩んだことがあります。
原因はOS側で別のJavaプロセスが8080ポートを掴みっぱなしだっただけで、lsof -i :8080(Windowsならnetstat -ano | findstr 8080)で確認すれば一発でした。
Tomcatのトラブルシュートでは、まず「どのプロセスがそのポートを使っているか」をOSレイヤーで確認するクセを付けると詰まる時間が大幅に減ります。
Tomcat 11系のインストール手順(macOS / Windows)
Apache Tomcatは公式サイト(tomcat.apache.org)から無料で入手できます。
Jakarta EE 11対応の Tomcat 11.0系を選んでください(Tomcat 10.1系も現行サポート対象ですが、新規学習には最新の11.0系を推奨。
Tomcat 9系はjavax.servlet時代の最終版)。
- macOS:
brew install tomcatが最短です。Homebrewが入っていれば1コマンドで導入でき、起動はbrew services start tomcat。 - Windows: 公式サイトから
apache-tomcat-11.0.x-windows-x64.zipをダウンロード→任意の場所に展開→bin\startup.batを実行。サービスとして登録するMSIインストーラもあります。 - 動作確認:
http://localhost:8080/にアクセスし、Tomcatのデフォルト画面が出れば成功。
サーブレットの基本的な実装
実際にJavaサーブレットをどのように実装するのか見ていきましょう。ここでは、基本となるクラスやメソッドについて解説します。
コードの例を見ながら、具体的なイメージをつかんでください。
最小サーブレットを動かす7ステップ(Hello Worldを5分で)
タイトルにある「7ステップ」を最初に体験しておくと、以降のコード解説が腹落ちしやすくなります。
Tomcat 11とJDK 17以上が手元にある前提で、最短ルートを示します。
- JDK 17以上をインストール:
java -versionで確認。 - Tomcat 11系をダウンロード: 公式サイトから
apache-tomcat-11.x.zipを取得し任意の場所に展開。 - プロジェクトフォルダを用意:
hello/WEB-INF/classes/とhello/WEB-INF/web.xmlを作成。 - HelloServlet.java を書く:
@WebServlet("/hello")付きクラスをWEB-INF/classes/配下に配置(パッケージなし)。 - コンパイル:
javac -cp "$CATALINA_HOME/lib/servlet-api.jar" HelloServlet.javaを実行。Tomcat 10以降はjakarta.servletをインポートする点に注意。 - デプロイ:
helloフォルダごと$CATALINA_HOME/webapps/にコピー。 - 起動と確認:
$CATALINA_HOME/bin/startup.sh(Windowsはstartup.bat)で起動後、ブラウザでhttp://localhost:8080/hello/helloにアクセス。「Hello, Servlet!」が表示されれば完了。
この7ステップで動く最小単位を体験しておくと、続くHttpServlet継承やdoGetの説明が「あの動いた仕組みのこの部分か」と理解できるようになります。
HttpServletクラスの継承
Javaサーブレットを作成するには、jakarta.servlet.http.HttpServlet(Tomcat 9以前は javax.servlet.http.HttpServlet)というクラスを継承する必要があります。
このクラスには、Webアプリケーションで必要な機能があらかじめ用意されています。
HttpServletを継承することで、HTTPプロトコルに特化した処理を簡単に行えるようになります。自分で一から通信の仕組みを作る必要はありません。
// Tomcat 10系(Jakarta EE 9以降)
import java.io.IOException;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
// ※Tomcat 9以前を使う場合は import を javax.servlet.* に置き換える
public class SampleServlet extends HttpServlet {
// この中に処理を記述していく
}
クラス宣言でextends HttpServletと記述するのが、Javaサーブレット作成の第一歩です。
doGetメソッドとdoPostメソッド
HttpServletクラスを継承したら、次にdoGetメソッドやdoPostメソッドをオーバーライドして、具体的な処理を記述します。
- doGetメソッド: HTTPのGETリクエストを処理します。URLを直接入力したり、リンクをクリックしたりした場合に使われます。
- doPostメソッド: HTTPのPOSTリクエストを処理します。フォームからデータを送信する場合などによく利用されます。
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// GETリクエストに対する処理をここに書く
response.setContentType("text/html; charset=UTF-8");
response.getWriter().println("<html><body><h1>Hello, World! (GET)</h1></body></html>");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// POSTリクエストに対する処理をここに書く
response.setContentType("text/html; charset=UTF-8");
response.getWriter().println("<html><body><h1>Hello, World! (POST)</h1></body></html>");
}
doGetとdoPostは、それぞれ異なる種類の要求に対応します。どちらのメソッドを実装するかは、アプリケーションの設計によって決まります。
なお上記コードはTomcat 10系を想定しており、必要なimportは jakarta.servlet.* です(Tomcat 9以前なら javax.servlet.*)。
サーブレットのライフサイクル(init・service・destroy)
サーブレットには決まった生存期間(ライフサイクル)があり、サーブレットコンテナが以下の3つのメソッドを適切なタイミングで呼び出します。
- init(): 既定ではサーブレットが最初にリクエストを受けたときに1度だけ呼ばれる初期化処理です(
loadOnStartupを指定するとアプリケーション起動時に呼ばれます)。 - service(): リクエストのたびに呼ばれ、内部でHTTPメソッドに応じて
doGetやdoPostに処理を振り分けます。 - destroy(): サーブレットコンテナの終了時やアプリケーション停止時に1度だけ呼ばれる終了処理です。
サーブレットはコンテナ内で通常1つのインスタンスが使い回されるシングルトンとして動作します。
そのため、インスタンス変数(フィールド)への書き込みは複数のリクエストから同時にアクセスされる可能性があり、スレッドセーフではありません。
リクエストごとに変化する値は、インスタンス変数ではなくローカル変数として扱いましょう。
リクエスト(HttpServletRequest)とレスポンス(HttpServletResponse)
doGetやdoPostメソッドの引数に注目してください。
HttpServletRequestとHttpServletResponseという2つのオブジェクトがあります。
これらはJavaサーブレットにおいて重要です。
- HttpServletRequest: ブラウザから送られてきた情報を持っています。
- フォームから送信された値の取得 (
getParameterメソッド) - リクエストURLやヘッダー情報の取得
- セッションやCookieの操作
- フォームから送信された値の取得 (
- HttpServletResponse: ブラウザに返す情報を設定します。
- 返すコンテンツの種類の指定 (
setContentTypeメソッド) - HTMLなどを書き出すための出力ストリームの取得 (
getWriterメソッド) - 別のページへリダイレクトする指示 (
sendRedirectメソッド)
- 返すコンテンツの種類の指定 (
例えば、フォームから送信された名前を取得して表示する場合、以下のように記述します。
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 送信された文字コードを設定
request.setCharacterEncoding("UTF-8");
// "username"という名前のフォーム要素の値を取得
String name = request.getParameter("username");
// レスポンスの準備
response.setContentType("text/html; charset=UTF-8");
// 画面に出力
response.getWriter().println("<html><body>こんにちは、" + name + "さん!</body></html>");
}
requestから情報を受け取り、responseを使って情報を返すのが基本です。
サーブレットの設定とデプロイ
Javaサーブレットのコードを作成したら、サーブレットコンテナに「このURLにアクセスが来たら、このサーブレットを動かしてください」と教える必要があります。
その設定方法と、アプリケーションを配備(デプロイ)する手順を見ていきましょう。
javax.servlet → jakarta.servlet 移行ガイド(既存プロジェクトを更新する場合)
既存のサーブレットプロジェクトをTomcat 10系(Jakarta EE 10)にアップデートする際は、ソース全体の javax.servlet を jakarta.servlet に置換する作業が発生します。
判断基準と移行手段を整理します。
- 新規プロジェクト: 迷わず Tomcat 11.0系 +
jakarta.servletを選ぶ(Jakarta EE 11対応)。今後のJDK・フレームワーク更新と整合します。 - 既存プロジェクトの保守: 当面動かすだけなら Tomcat 9.0系 +
javax.servlet継続も選択肢。Tomcat 9はJakarta EE 8相当でセキュリティアップデートも継続しています。 - 移行する場合: IDE(IntelliJ IDEA / Eclipse)の「プロジェクト全体置換」で
javax.servlet→jakarta.servletを一括変換するのが基本。Apache公式のTomcat Migration Tool for Jakarta EEを使えば、コンパイル済みのWAR/JARを自動で書き換えることもできます。
注意点として、Spring Boot 3系もJakarta EE 9以降のみサポートし、Spring Boot 2.7系がjavax名前空間の最終バージョンです。
フレームワーク側のメジャーアップデートと同時に移行するのが現実的です。
移行プロジェクトの一次情報:Spring Boot 2.7→3.0で実際にハマった点
私が担当したSpring Boot 2.7→3.0アップグレードでは、javax.servlet→jakarta.servlet のソース置換以上に、依存ライブラリ側のJakarta対応待ちに時間を取られました。
具体的には、当時利用していた古い spring-boot-starter-validation のバリデーション実装系ライブラリと、独自開発した HandlerInterceptor 実装の修正が必要でした。
教訓として、移行前に mvn dependency:tree または gradle dependencies で全依存を洗い出し、各ライブラリの「Jakarta EE 9対応バージョン」が出ているかを先にリスト化しておくと安全です。
Spring公式のSpring Boot 3.0 Migration GuideとOpenRewriteのorg.openrewrite.java.migrate.JakartaEEレシピを併用すると、置換漏れを大幅に減らせます。
アノテーションによる設定方法(@WebServlet)
現在の主流は @WebServlet アノテーションを使う方式です。
Servlet 3.0以降で導入され、設定ファイル不要でJavaコードだけでURLマッピングを完結できます。
Jakarta EE 9以降ではパッケージが jakarta.servlet.annotation.WebServlet に変わっている点だけ注意してください。
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
// ... 他のimport文
@WebServlet("/sample")
public class SampleServlet extends HttpServlet {
// ... doGetやdoPostメソッドの実装
}
クラス宣言の直前に@WebServlet("/sample")と書くだけで、web.xmlに書いていた設定と同じ意味になります。
設定ファイルが要らずコードが簡潔になるため、新規プロジェクトはこちらが第一選択です。
web.xml(deployment descriptor)の設定
アノテーション登場以前から使われている設定方法が、web.xml を使う方式です。レガシーシステムや、外部から設定を切り替えたい場合に今も現役です。
WEB-INFという特別なディレクトリの中にweb.xmlを配置します。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_1.xsd"
version="6.1">
<!-- サーブレットの定義 -->
<servlet>
<servlet-name>SampleServlet</servlet-name>
<servlet-class>com.example.SampleServlet</servlet-class>
</servlet>
<!-- サーブレットとURLパターンのマッピング -->
<servlet-mapping>
<servlet-name>SampleServlet</servlet-name>
<url-pattern>/sample</url-pattern>
</servlet-mapping>
</web-app>
この設定により「/sample」というURLへのアクセスがあった場合に、「com.example.SampleServlet」クラスが実行されます。
Jakarta EE 11(Servlet 6.1)では上の例の web-app_6_1.xsd(version="6.1")、Jakarta EE 10(Servlet 6.0)では web-app_6_0.xsd と名前空間 https://jakarta.ee/xml/ns/jakartaee を使う点に注意してください(Tomcat 9系までは http://xmlns.jcp.org/xml/ns/javaee の web-app_4_0.xsd)。
WARファイルの作成とデプロイ
開発したWebアプリケーションをサーブレットコンテナで動かすには、所定の形式にまとめる必要があります。
その形式がWAR(Web Application Archive)ファイルです。
WARファイルは、Javaクラス(.classファイル)、JSPファイル、HTMLファイル、画像、設定ファイル(web.xmlなど)を、特定のディレクトリ構造でZIP形式に圧縮したものです。
作成したWARファイルを、Tomcatのwebappsディレクトリに配置するだけで、Tomcatが自動的にファイルを展開し、アプリケーションが利用可能な状態になります。
この作業を「デプロイ」と呼びます。
統合開発環境(IDE)のEclipse・IntelliJ IDEA・Visual Studio Code(Java拡張)を使えば、WARファイルの作成からデプロイまでを簡単に行えます。
サーブレットとSpring Bootの違い・どちらを学ぶべきか
「いまから学ぶならSpring Bootだけで十分では?」とよく聞かれます。
結論は 「Spring Bootを実務で使うとしても、サーブレットの基礎知識は必須」です。理由をまとめます。
- Spring Bootの内部はサーブレット: Spring MVCの
DispatcherServletはHttpServletを継承しており、フィルター・リスナー・HttpServletRequestはそのまま登場します。 - トラブルシュートで差が出る: 文字化け・セッション切れ・CORS・フィルター順序など、原因を追うときは結局サーブレット仕様の理解が要ります。
- 役割が違う: サーブレットは「HTTPを扱う最小APIの仕様」、Spring Bootは「サーブレット+多数のライブラリを束ねた開発フレームワーク」。比較というより階層関係です。
学習順は「サーブレットでHTTPの動きを把握 → Spring Bootで生産性を上げる」が最短ルートです。
逆順で始めると、Springが裏で何をしているか分からないままアプリを書くことになり、エラー対応で詰まりがちになります。
サーブレットの応用と実践
基本を押さえたら、次はデータベース連携やフォームデータ処理といった実践的な使い方に進みましょう。多くのWebアプリで必要になる定番機能です。
データベース連携(JDBC)
多くのWebアプリは商品やユーザーの情報をデータベースに保存します。
サーブレットからDBへアクセスするにはJDBC(Java Database Connectivity)というAPIを使います。
JDBCを使ったデータベース連携の一般的な手順は以下の通りです。
- JDBCドライバの読み込み: 使用するデータベース製品(MySQL, PostgreSQLなど)に応じたJDBCドライバをプロジェクトに追加し、読み込みます。
- データベースへの接続: 接続URL、ユーザー名、パスワードを指定して、データベースへの接続を確立します。
- SQL文の実行:
PreparedStatementオブジェクトなどを用いて、SQL文(SELECT, INSERTなど)を作成し、実行します。 - 結果の処理: SELECT文の場合は、
ResultSetオブジェクトから実行結果を取得し、処理します。 - リソースの解放: データベースとの接続や
PreparedStatementなどを、必ずclose()メソッドで解放します。
// 実際のコードではtry-with-resources文を使うのが一般的です
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 1, 2. ドライバの読み込みと接続
Class.forName("com.mysql.cj.jdbc.Driver");
con = DriverManager.getConnection("jdbc:mysql://localhost/mydb", "user", "password");
// 3. SQL文の準備と実行
String sql = "SELECT id, name FROM users WHERE id = ?";
ps = con.prepareStatement(sql);
ps.setInt(1, 100);
rs = ps.executeQuery();
// 4. 結果の処理
if (rs.next()) {
String name = rs.getString("name");
// ...
}
} catch (Exception e) {
// エラー処理
} finally {
// 5. リソースの解放
// ...
}
フォームデータの処理
HTMLのフォームから送信されたデータは、HttpServletRequestオブジェクトを使って受け取ります。
request.getParameter("name属性値")メソッドで、テキストボックスやセレクトボックスなどの値を取得できます。
送信データの文字コードを正しく扱うことが重要です。日本語などのマルチバイト文字を扱う場合、文字化けが発生することがあります。
これを防ぐには、getParameterを呼び出す前に、request.setCharacterEncoding("UTF-8")を実行する必要があります。
// POSTリクエストの場合、最初に文字コードを設定
request.setCharacterEncoding("UTF-8");
// フォームから送信された値を取得
String name = request.getParameter("username");
String ageStr = request.getParameter("age");
String[] hobbies = request.getParameterValues("hobby"); // チェックボックスなど複数選択可能な場合
// 必要に応じて型変換やバリデーションを行う
int age = Integer.parseInt(ageStr);
// ... 取得したデータを使った処理
フィルターとリスナー
サーブレットには、アプリケーション全体で共通の処理を行いたい場合に便利な「フィルター」と「リスナー」という仕組みがあります。
- フィルター: サーブレットがリクエストを処理する前後に、共通の処理を挟み込むことができます。すべてのリクエストに対して文字コードを設定したり、ログイン状態をチェックしたりするのに便利です。
- リスナー: Webアプリケーションの特定のイベント(ライフサイクルイベント)を検知して、処理を実行します。たとえば、アプリケーションの起動時にデータベース接続を初期化したり、セッションが作成されたときにログを記録したりするのに使われます。
これらの仕組みを活用すると、コードの重複を減らし、より構造化されたアプリケーションを構築できます。
Tomcat 10系では jakarta.servlet.Filter / jakarta.servlet.ServletContextListener として実装します。
サーブレットのセキュリティ
Webアプリケーションを公開する上で、セキュリティ対策は絶対に欠かせません。
Javaサーブレットを開発する際にも、代表的な脆弱性とその対策を理解しておく必要があります。ここでは、特に重要な3つの脅威について解説します。
クロスサイトスクリプティング(XSS)対策
クロスサイトスクリプティング(XSS)は、悪意のあるユーザーがWebページに不正なスクリプトを埋め込み、他のユーザーのブラウザ上で実行させる攻撃です。
これにより、個人情報が盗まれたり、意図しない操作をさせられたりする危険があります。
対策
ユーザーからの入力値を画面に出力する際には、必ず「エスケープ処理」を行います。
エスケープ処理とは、<や>といったHTMLタグとして特別な意味を持つ文字を、単なる文字列として表示されるように変換する(例: <を<に)処理です。
専用のライブラリ(例: OWASP Java Encoder)を使うことで、安全にエスケープ処理を実装できます。
SQLインジェクション対策
SQLインジェクションは、アプリケーションが想定していない不正なSQL文を、攻撃者によって実行されてしまう脆弱性です。
これにより、データベース内の情報が盗まれたり、改ざん・削除されたりする可能性があります。
対策
SQL文を組み立てる際に、文字列連結で変数を埋め込むのは危険です。
必ず「プリペアードステートメント(PreparedStatement)」を使用してください。
PreparedStatementは、SQL文の骨格を先にデータベースに送り、後から値を「?」(プレースホルダ)の部分に安全な形で埋め込む仕組みです。
これにより、入力値がSQL文の一部として解釈されるのを防ぎます。
セッションハイジャック対策
セッションハイジャックは、攻撃者が何らかの方法で他人のセッションIDを盗み出し、そのユーザーになりすまして不正な操作を行う攻撃です。
対策
- 通信の暗号化(HTTPS): SSL/TLSを用いて通信全体を暗号化し、ネットワーク経路上でのセッションIDの盗聴を防ぎます。
- セッションIDの再生成: ユーザーがログインに成功したタイミングなど、権限状態が変わる際には、
request.changeSessionId()メソッド(Servlet 3.1以降・Jakarta Servlet 6.0でも引き続き利用可能)を呼び出して、セッションIDを新しいものに切り替えます。 - 推測困難なセッションID: サーブレットコンテナが生成するセッションIDは十分に複雑ですが、自前で実装する場合は推測されないように注意が必要です。
これらの対策を怠ると深刻な被害につながる可能性があります。安全なWeb開発の必須事項として、必ず押さえておきましょう。
サーブレットでよくあるエラーと対処
初学者が最初の数日で必ず踏むエラーをまとめておきます。原因と対処を1セットで覚えておくと、つまずきから抜け出すまでの時間が大幅に短くなります。
404 Not Found(URLにアクセスしても表示されない)
原因の多くは、@WebServletのURLパターンとアクセスURLの不一致、または webapps 配下のフォルダ名(コンテキストパス)の認識違いです。
http://localhost:8080/<コンテキスト>/<URLパターン> という構造を意識しましょう。
Tomcatのログ(logs/catalina.out)にデプロイ完了メッセージが出ているかも併せて確認します。
ClassNotFoundException / NoClassDefFoundError
Tomcat 10系で javax.servlet.* をインポートしているコードを動かすと発生する典型例です。
Tomcat 10以降は jakarta.servlet.* のみ提供されるため、importを書き換えるか、Tomcat 9系(javax)に合わせる必要があります(詳細は前述の「javax.servlet → jakarta.servlet 移行ガイド」を参照)。
フォーム送信で日本語が文字化けする
doPostの冒頭でrequest.setCharacterEncoding("UTF-8")を呼び忘れているケースが大半です。
getParameterより前に必ず実行してください。
レスポンス側もresponse.setContentType("text/html; charset=UTF-8")をセットします。
現場体験談:setCharacterEncodingしても文字化けが直らなかった話
過去に担当したプロジェクトで、doPost側で正しく setCharacterEncoding("UTF-8") を呼んでいるのに、GETパラメータだけ文字化けする事象がありました。
原因はTomcatの conf/server.xml の <Connector> 要素に URIEncoding="UTF-8" が設定されていなかったこと。
Tomcat 8以降はデフォルトUTF-8ですが、古い構成ファイルを引き継いだ環境ではISO-8859-1のままになっているケースがあります。
doPostのsetCharacterEncodingはあくまで「リクエストボディ用」で、GETクエリのデコードはConnector側が担当する点を覚えておくと、原因切り分けが速くなります。
よくある質問
Q1. Javaサーブレットとは何ですか?
A1. サーバー側で動くJavaプログラムです。Webブラウザからのリクエストを受け取り、動的なWebページを生成してレスポンスを返す役割を担います。
Q2. サーブレットとJSPの違いは何ですか?
A2. サーブレットはロジック記述が得意でデータベース連携や複雑な計算を担当し、JSPはHTMLに埋め込む形で画面表示を担当します。処理はサーブレット、表示はJSPという役割分担がMVCモデルの基本です。
Q3. サーブレットを動かすには何が必要ですか?
A3. Tomcatなどのサーブレットコンテナ(Webコンテナ)が必要です。作成したサーブレットは単体では動作せず、コンテナに配置(デプロイ)することでWebアプリケーションとして公開できます。
Q4. javax.servletとjakarta.servletの違いは何ですか?
A4. サーブレットAPIを含むJava EEがOracleからEclipse Foundationへ移管されて「Jakarta EE」になり、Jakarta EE 9(2020年)でパッケージ名が変更されました。Jakarta EE 9以降はjakarta.servlet、それ以前(Jakarta EE 8を含む)はjavax.servletを使います。Tomcat 10系以降はjakarta名前空間のみサポートします。
Q5. doGetとdoPostはどう使い分けますか?
A5. doGetはURLへの直接アクセスやリンククリック時のGETリクエストを処理し、doPostはフォーム送信などのPOSTリクエストを処理します。アプリケーションの設計に応じて使い分けます。
Q6. サーブレットのインスタンス変数を使う際の注意点は?
A6. サーブレットは通常1つのインスタンスが使い回されるシングルトンとして動作するため、インスタンス変数への書き込みはスレッドセーフではありません。リクエストごとに変化する値はローカル変数として扱う必要があります。
Q7. URLにアクセスしてもサーブレットが表示されないのはなぜですか?
A7. 原因の多くは@WebServletのURLパターンとアクセスURLの不一致、またはwebapps配下のフォルダ名(コンテキストパス)の認識違いです。Tomcatのログにデプロイ完了メッセージが出ているかも確認してください。
Q8. ClassNotFoundExceptionが発生するのはなぜですか?
A8. Tomcat 10系でjavax.servlet.*をインポートしているコードを動かすと発生する典型例です。Tomcat 10以降はjakarta.servlet.*のみ提供されるため、importを書き換える必要があります。
Q9. フォーム送信で日本語が文字化けするのはなぜですか?
A9. doPostの冒頭でrequest.setCharacterEncoding("UTF-8")を呼び忘れているケースが大半です。getParameterより前に必ず実行し、レスポンス側もresponse.setContentType("text/html; charset=UTF-8")をセットしてください。
まとめ
Javaサーブレットは、現代のWebフレームワークを理解するための必須の基礎知識です。
なぜなら、SpringやJakarta EEといった高機能なフレームワークも、その内部ではサーブレットの技術が基盤として動いているからです。
サーブレットがリクエストを受け取り、レスポンスを返すという基本的なサイクルを理解していると、フレームワークが裏側で何をしているのかを深くイメージできるようになります。
結果として、エラー時のトラブルシュート力が大きく上がります。
Webアプリの根本を理解しておくと、特定フレームワークに依存しない応用力も身につくでしょう。
JavaでのWeb開発を志すなら、ぜひサーブレットの学習から始めてみてください。
サーブレット学習の次のステップ
- JSPと組み合わせたMVCパターン: 画面生成をJSPに分離して保守性を上げる。
- Spring Boot: サーブレットの上に立つ生産性向上フレームワーク。実務では事実上の標準。
- Jakarta EEの他のAPI: CDI・JPA・JAX-RSなど。エンタープライズ開発の幅が広がる。
- HTTP/Cookie/CORSの仕様: サーブレットのリクエスト/レスポンスを深く扱うために必要。
| REV. | DATE | 内容 |
|---|---|---|
| 6 | 2026-10-16 | 図解を描き直し(図面のデザインにそろえた) |
| 5 | 2026-10-14 | 雰囲気だけの挿絵を外した |
| 4 | 2026-10-04 | 記載内容の事実確認と修正 |
| 3 | 2026-08-30 | 2026年8月時点の情報で全体を見直し(古い記述を6件更新) |
| … ほか1回の改訂 | ||
| 1 | 2025-10-21 | 初版 |



