この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:3
ページ更新者:T
更新日時:2026-06-11 07:07:02

タイトル: Servlet(サーブレット)
SEOタイトル: Java Servlet 完全ガイド (HttpServlet / ライフサイクル / Jakarta EE 移行)

この記事の要点
  • Servlet = Java で動的 Web レスポンスを返すサーバサイドコンポーネント。HttpServlet を継承して doGet / doPost を実装
  • マッピングは web.xml または @WebServlet("/path") アノテーション
  • ライフサイクル: init() → service() → doGet/doPost() → destroy()
  • Servlet 4.0 までは javax.servletJakarta EE 9+ は jakarta.servlet に移行
  • 現代の Web アプリは Servlet を直接書かず Spring MVC (内部で DispatcherServlet) / Spring Boot / Vert.x が主流

Servlet とは

Servlet (サーブレット) はサーバ上で動作する Java のクラスで、HTTP リクエストを受け取り動的なレスポンスを返します。1997 年に Sun (現 Oracle) が発表した、Java サーバサイドの基礎仕様です。

Tomcat / Jetty / WildFly / GlassFish などのサーブレットコンテナ上で動作します。

最小サンプル

import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
        resp.setContentType("text/html; charset=UTF-8");
        try (PrintWriter out = resp.getWriter()) {
            out.println("

Hello, Servlet!

"); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { String name = req.getParameter("name"); resp.setContentType("text/plain; charset=UTF-8"); resp.getWriter().println("Hello, " + name); } }

Servlet のライフサイクル

フェーズメソッド呼ばれるタイミング
初期化init()★ サーブレット生成直後 (1 度だけ)
サービスservice()リクエストごと → doGet/doPost にディスパッチ
処理doGet / doPost / doPut / doDeleteHTTP メソッドに応じて
破棄destroy()★ サーブレットコンテナ停止時 / リロード時

重要: Servlet インスタンスはシングルトンで、複数スレッドが同時に service() を呼びます。インスタンスフィールドは thread-unsafe なので使わないか同期します。

@WebServlet("/lifecycle")
public class LifecycleServlet extends HttpServlet {
    private DataSource ds;
    // ⚠️ インスタンスフィールドはスレッド共有

    @Override
    public void init() {
        // 1 度だけ呼ばれる → 初期化に最適
        ds = lookupDataSource();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        // ★ ローカル変数のみ使う → スレッドセーフ
        try (Connection conn = ds.getConnection()) { ... }
    }

    @Override
    public void destroy() {
        // クリーンアップ (コネクションプール close 等)
    }
}

HttpServletRequest と HttpServletResponse

@WebServlet("/users")
public class UserServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException, ServletException {

        // クエリパラメータ
        String name = req.getParameter("name");
        String[] tags = req.getParameterValues("tag");

        // ヘッダ
        String userAgent = req.getHeader("User-Agent");

        // パス情報
        String uri = req.getRequestURI();         // /app/users
        String path = req.getServletPath();        // /users
        String method = req.getMethod();           // GET

        // セッション
        HttpSession session = req.getSession();
        session.setAttribute("user", name);
        Object u = session.getAttribute("user");

        // Cookie
        Cookie[] cookies = req.getCookies();

        // Body (POST)
        BufferedReader reader = req.getReader();

        // === レスポンス ===
        resp.setStatus(200);  // HttpServletResponse.SC_OK
        resp.setContentType("application/json; charset=UTF-8");
        resp.setCharacterEncoding("UTF-8");

        // ヘッダ
        resp.setHeader("Cache-Control", "no-cache");
        resp.addCookie(new Cookie("session_id", "abc"));

        // ボディ
        resp.getWriter().println("{\"name\": \"" + name + "\"}");

        // リダイレクト
        resp.sendRedirect("/login");

        // エラー
        resp.sendError(404, "Not Found");

        // フォワード (内部転送)
        req.getRequestDispatcher("/WEB-INF/views/index.jsp").forward(req, resp);
    }
}

マッピング: web.xml vs アノテーション

web.xml (古典的)




    
        HelloServlet
        com.example.HelloServlet
        
            greeting
            Hello
        
        1
    

    
        HelloServlet
        /hello
    

@WebServlet アノテーション (Servlet 3.0+)

@WebServlet(
    name = "HelloServlet",
    urlPatterns = {"/hello", "/greet"},
    initParams = {
        @WebInitParam(name = "greeting", value = "Hello")
    },
    loadOnStartup = 1
)
public class HelloServlet extends HttpServlet { ... }

セッション管理

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
        String user = req.getParameter("username");
        String pass = req.getParameter("password");

        if (authenticate(user, pass)) {
            HttpSession session = req.getSession(true);  // 無ければ作成
            session.setAttribute("user", user);
            session.setMaxInactiveInterval(30 * 60);     // 30 分
            resp.sendRedirect("/dashboard");
        } else {
            resp.sendRedirect("/login?error=1");
        }
    }
}

@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
        HttpSession session = req.getSession(false);
        if (session != null) {
            session.invalidate();
        }
        resp.sendRedirect("/");
    }
}

Filter とListener

// Filter: リクエスト前後の共通処理
@WebFilter("/*")
public class LoggingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        long start = System.currentTimeMillis();
        try {
            chain.doFilter(req, res);    // 次のフィルタ or サーブレットへ
        } finally {
            System.out.println("elapsed: " + (System.currentTimeMillis() - start));
        }
    }
}

// Listener: 各種イベント (起動 / セッション生成 / 属性変更)
@WebListener
public class AppListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // アプリ起動時 (DB プール構築など)
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        // アプリ停止時
    }
}

JSP との関係

JSP (JavaServer Pages) はHTML 中に Java コードを埋め込めるテンプレート技術で、内部的にはServlet にコンパイルされる関係です。

<%-- WEB-INF/views/users.jsp --%>
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="jakarta.tags.core" prefix="c" %>


    

Users

  • ${user.name}

典型的な MVC では Servlet がコントローラ役、JSP が View 役を担います。

javax.servlet → jakarta.servlet 移行 (Jakarta EE 9+)

Oracle から Eclipse Foundation への Java EE 移管 (Jakarta EE) に伴い、Java EE 8 / Servlet 4.0 までは javax.servletJakarta EE 9+ / Servlet 5.0+ は jakarta.servlet に変わりました。

EE バージョンServletパッケージサーバ例
Java EE 73.1javax.servletTomcat 8 / WildFly 8
Java EE 84.0javax.servletTomcat 9 / WildFly 18
★ Jakarta EE 95.0jakarta.servletTomcat 10 / WildFly 27
Jakarta EE 106.0jakarta.servletTomcat 10.1
Jakarta EE 116.1jakarta.servletTomcat 11

現代の選択肢: Spring MVC / Spring Boot

Spring Framework は内部に DispatcherServlet (フロントコントローラ) を持ち、Servlet 仕様の上に MVC を構築しています。現代の Java Web 開発は素の Servlet を書くことは稀で、Spring Boot / Quarkus / Micronaut / Vert.x が主流です。

// Spring Boot Controller (Servlet を直接書かない)
@RestController
@RequestMapping("/api/users")
public class UserController {

    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.findById(id);
    }

    @PostMapping
    public User create(@RequestBody UserCreateRequest req) {
        return userService.create(req);
    }
}
// → Spring が内部で DispatcherServlet → HandlerMapping → このメソッドへルーティング

FAQ

Q: いまから Servlet を学ぶ価値はある?
A: 直接書く機会は減ったが、Spring / Tomcat の動作理解の基礎なので仕様の理解は有益。新規プロジェクトでは Spring Boot を直接学んだ方が実用的。

Q: javax.servlet ライブラリが見つからない
A: Jakarta EE 移行で jakarta.servlet-api に変わりました。spring-boot-starter-web 3.x も Jakarta 系。

Q: Servlet のスレッド数は?
A: コンテナ (Tomcat) のスレッドプール (デフォルト 200) で同時処理されます。server.tomcat.threads.max で調整。