サーブレット完全ガイド:基礎から実装まで

1.サーブレット概要

1.1 サーブレットとは

サーブレット(Servlet)はServer Appletの略称で、Javaで記述されWebサーバー上で実行されるプログラムです。Webブラウザやその他のHTTPクライアントからのリクエストとHTTPサーバー上のデータベースまたはアプリケーション間の中間層として機能します。サーブレットを使用することで、Webフォームからのユーザー入力を収集したり、データベースやその他のソースからのレコードを表示したり、動的にWebページを作成したりできます。

1.2 サーブレットの利用方法

サーブレット技術の核心はServletインターフェースであり、サーブレットとサーブレットコンテナ間の契約を定義します。サーブレットコンテナはサーブレットクラスをメモリに読み込み、サーブレットインスタンスを生成し、その具体的なメソッドを呼び出します。したがって、これはすべてのサーブレットクラスが直接または間接的に実装しなければならないインターフェースです。

1.3 サーブレットインターフェースの継承構造

  • Servletインターフェース:サーブレットプログラムのアクセス仕様を定義するのみ
  • GenericServlet抽象クラス:Servletインターフェースを実装し、多くの空実装を行い、ServletConfigクラスの参照を保持し、いくつかのServletConfigの使用方法を提供
  • HttpServlet抽象クラス:serviceメソッドを実装し、リクエストディスパッチ処理を実現

2.サーブレットクイックスタート

2.1 Mavenプロジェクトの作成

2.2 依存関係の追加

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
    <scope>provided</scope>
</dependency>





<build>
    <finalName>webapp-example</finalName>
    <plugins>
      <plugin>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <version>9.4.44.v20210927</version>
        <configuration>
          <httpConnector>
            <port>8080</port>
          </httpConnector>
        </configuration>
      </plugin>
    </plugins>
  </build>

2.3 最初のサーブレット

package com.example.web;

import javax.servlet.*;
import java.io.IOException;

public class FirstServlet implements Servlet {
    
    private ServletConfig config;
    
    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        System.out.println("サーブレットが初期化されました");
    }

    @Override
    public ServletConfig getServletConfig() {
        return config;
    }

    @Override
    public void service(ServletRequest request, ServletResponse response) 
         throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().write("<h1>こんにちは、サーブレットの世界へようこそ!</h1>");
    }

    @Override
    public String getServletInfo() {
        return "初めてのサーブレット";
    }

    @Override
    public void destroy() {
        System.out.println("サーブレットが破棄されました");
    }
}

2.4 サーブレットの設定

  • web.xmlでの設定
<!-- 
  サーブレットの設定
  servlet-name: サーブレットの名前
  servlet-class: サーブレットの完全修飾クラス名
  url-pattern: サーブレットにアクセスするURL
 -->
<servlet>
    <servlet-name>demoServlet</servlet-name>
    <servlet-class>com.example.web.FirstServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>demoServlet</servlet-name>
    <url-pattern>/welcome</url-pattern>
</servlet-mapping>

2.5 テスト

ブラウザでローカルにアクセス: http://localhost:8080/welcome

3.サーブレットのライフサイクル

簡単に言えば、サーブレットのライフサイクルとは、サーブレットクラスのオブジェクトがいつ作成され、いつ対応するメソッドが呼び出され、いつ破棄されるかということです。

3.1 サーブレットライフサイクルにおける重要なメソッド

  • コンストラクタ:サーブレットを作成するときに呼び出されます。デフォルトでは、最初にサーブレットにアクセスしたときにサーブレットオブジェクトが作成されます。このときにコンストラクタが1回だけ呼び出され、サーブレットオブジェクトがシングルトンであることが証明されます。
  • initメソッド:サーブレットオブジェクトの作成後に呼び出され、1回だけ呼び出されます。
  • serviceメソッド:サービスを提供するメソッドで、ユーザーのリクエストを受け取り、処理し、ユーザーに応答します。リクエストを送信するたびにserviceメソッドが呼び出され、呼び出されるたびにserviceメソッドが実行されます。
  • destroyメソッド:破棄メソッド。サーブレットオブジェクトが破棄されるときに呼び出されます。例えば、サーバーを停止したり、サーバーを再デプロイしたりすると、サーブレットオブジェクトが破棄され、1回だけ呼び出されます。

3.2 サーブレットライフサイクルのテスト

public class LifecycleServlet implements Servlet {

    public LifecycleServlet(){
        System.out.println("LifecycleServletコンストラクタが実行されました........");
    }

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        System.out.println("initメソッドが実行されました........");
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) 
         throws ServletException, IOException {
        System.out.println("serviceメソッドが実行されました........");
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void destroy() {
        System.out.println("destroyメソッドが実行されました........");
    }
}

4.サーブレットの継承

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/demo")
public class CustomHttpServlet extends HttpServlet {
    
    //HTTPリクエストにはGETとPOSTの2種類の方式がある
    //doGetとdoPostメソッドはServiceメソッド内で実装されている
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
         throws ServletException, IOException {
        //POSTリクエストも同じ処理を行う
        processRequest(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
         throws ServletException, IOException {
        processRequest(request, response);
    }
    
    private void processRequest(HttpServletRequest request, HttpServletResponse response) 
         throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().println("HttpServletを継承したカスタムサーブレットです");
    }
}

5.フロントエンドからのデータ取得

サーブレットを学ぶ際は、リクエストとレスポンスという2つの概念に固執する必要があります。上記のコードは単にリクエストを受け取り、クライアントに応答しているだけです。リクエスト時にデータをサーブレットに渡していません。ここから、リクエスト時にフロントエンドからデータをサーブレットに渡し、サーブレットがそのデータを受け取る方法を学びます。

  • login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>ログインフォーム</title>
  </head>
  <body>
  <form action="auth" method="post">
    <label for="username">ユーザー名:</label>
    <input type="text" id="username" name="username"/><br>
    <label for="password">パスワード:</label>
    <input type="password" id="password" name="password"/><br>
    <input type="submit" value="ログイン">
  </form>
  </body>
</html>

サーブレットの実装例

public class AuthenticationServlet extends HttpServlet {
    
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
         throws ServletException, IOException {
        // リクエストの文字エンコーディングを設定
        request.setCharacterEncoding("UTF-8");
        
        // JSPページのinputタグで送信されたデータはすべてHttpServletRequestオブジェクトに保存される
        // フロントエンドのinputタグのname属性値を使って、フロントエンドから送信されたデータを取得
        String userId = request.getParameter("username");
        String userPass = request.getParameter("password");
        System.out.println("ユーザー名: " + userId + " パスワード: " + userPass);
        
        // サーブレットがクライアントにデータを返す際に日本語があると文字化けする
        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().write("ログイン情報 - ユーザー名: " + userId + " パスワード: " + userPass);
    }
}

6.文字化け問題の解決策

リクエスト時の文字化け問題:

// フロントエンドからデータをサーブレットに送信する際、POSTリクエストの場合、inputに日本語データを入力すると、サーブレットで受け取ったデータが文字化けする
request.setCharacterEncoding("UTF-8");

レスポンス時の文字化け問題:

// サーブレットがクライアントにデータを返す際に日本語があると文字化けする
response.setContentType("text/html;charset=UTF-8");

7.リダイレクトとフォワード

7.1 リダイレクト

  • とは何か

ユーザーがブラウザからリクエストを送信し、Tomcatサーバーがこのリクエストを受け取ると、ブラウザに302のステータスコードを送信し、リダイレクトパスを設定します。ブラウザがこの302のステータスコードを受け取ると、サーバーで設定されたパスを自動的に読み込みます。

あるページから別のページに移動する(使用例)、ログインページからメインページへの移動: login.jsp ⇒ AuthenticationServlet ⇒ main.jsp

  • 特徴:

①リダイレクトの過程はブラウザ(クライアント)の動作である ②実際にはブラウザが2回のリクエストを行う(ログインボタンをクリックしたときに2回のリクエストが発生)(それぞれloginとmain.jspへのリクエスト) ③前回のリクエストのrequestオブジェクトは失われる ④リダイレクトには非常に明確な特徴があり、ブラウザのURLが変わる

response.sendRedirect("main.jsp"); // この1行のコードだけで、このコードはdoGetまたはdoPostメソッド内に記述する必要があります

7.2 フォワード

  • とは何か

ユーザーがサーバーにデータリクエストを送信し、サーバーがこのリクエストを受け取ると、内部の方法(フォワード)を使用してこのリクエストを処理し、最終的にクライアントに応答します。

  • 特徴:

①フォワードはサーバーの動作である ②ブラウザのこの過程では1回の動作しか行わない ③フォワードではデータをrequestオブジェクトに含めることができる ④URLは一切変化しない

  • 核心コードも1行だけ
request.getRequestDispatcher("main.jsp").forward(request, response);
// この1行のコードでフォワードが行われ、URLは変わらないが、応答の結果は1回のリクエストで2つの作業をこなしている
  1. Requestオブジェクト

  • とは何か

ServletRequestはインターフェースで、ユーザーがサーバーにアクセスすると、サーバーはHTTPリクエストヘッダーを含むオブジェクトを生成します。HTTPプロトコルを使用しているため、このオブジェクトの名前はHttpServletRequestです。

  • 主なメソッド
getRequestURL() // 完全なURLを取得、つまり統一リソースロケーター
getRequestURI() // リソースの名前を取得、つまり統一リソース識別子
getQueryString() // URLのパラメータ部分を取得
getRemoteAddr() // クライアントのIPアドレスを返す
getRemoteUser() // クライアントのユーザーを返す
getRemotePort() // クライアントのホストのポート番号を返す
getRemoteHost() // クライアントのホストアドレスを返す
getCookies() // Cookieオブジェクトを取得
getSession() // セッションオブジェクトを取得
getLocalName() // Webサーバーホストの名前を取得
getServletContext() // コンテキストオブジェクトを取得
setCharacterEncoding() // 文字エンコーディングを設定
getParameter() // フロントエンドから渡されたデータを取得
setAttribute() // データをrequestオブジェクトに設定
getAttribute() // requestオブジェクトのデータを取得
  1. Responseオブジェクト

  • とは何か

レスポンスオブジェクトは、データをクライアントに返します。私たちのサーブレットは、リクエストとレスポンスという2つの点(Request、Response)に固執しています。

  • 主なメソッド
setHeader() // レスポンスヘッダーのパラメータを設定
setContentType() // 文字エンコーディングを設定
getWriter() // 文字出力ストリームオブジェクトを取得
addCookie() // ブラウザにCookieを追加
sendRedirect() // リダイレクト

10.Cookieとセッション

10.1 Cookie

  • とは何か
  1. cookieはブラウザに保存される
  2. cookieを使用するにはブラウザでcookieが有効になっていることを保証する必要があるため、一定の欠点がある。ブラウザでcookieが有効になっていない場合、cookieを使用できない
  3. cookieのサイズには制限があり、通常は4096バイト
  4. cookieの保存はキーと値のペアの形式で存在する
  • 主なメソッド
// 1. cookieのコンストラクタ、目的はcookieオブジェクトをインスタンス化すること
Cookie(String name, String value)
// 2. cookieを設定するメソッド
setValue(String value) // cookieの値を変更
getValue() // cookieの値を取得    
getName() // cookieのキーを取得    
setMaxAge(int time) // cookieの有効時間を設定
// 3. cookieをブラウザに送信する
response.addCookie(Cookie cookie);
// 4. すべてのcookieを取得
request.getCookies();

10.2 セッション

  • なぜセッションを使用するのか?
  1. cookieに保存できるデータの型は限られており、文字列型のデータしか保存できない
  2. cookieのサイズには制限がある
  • とは何か?

サーバーに保存

ユーザーがHTTPリクエストをサーバーに送信すると、サーバーはそのリクエストにセッション識別子(通常はcookieに保存)が含まれているかどうかを確認します。もしなければ、新しいセッション(保存領域)を作成し、セッション識別子をクライアントに送信します。ブラウザがリクエストを送信する際にセッション識別子を持ち、サーバーはセッション識別子に基づいて対応するセッション(保存領域)を見つけることができます

セッションを使用する際には、通常cookieを有効にする必要があります。ブラウザでcookie機能が有効になっていない場合、HTMLのURLパラメータを渡した後、セッションを使用することができます

サイズの制限はない

情報の保存もキーと値のペアの形式で存在する

  • 主なメソッド
request.getSession(); // HttpSessionを返す
setAttribute(key, value); // 属性を設定 key, value
getAttribute(key); // keyの値を取得
removeAttribute(String key) // key値でデータを削除
invalidate(); // セッション内の変数をすべてクリア
setMaxInactiveInterval(int interval); // セッションの有効期間を設定、単位は秒
getMaxInactiveInterval(); // セッションの有効期間を取得
getId(); // セッションIDを取得

11.フィルターとリスナー

11.1 フィルター

  • フィルターとは何か

ブラウザがサーバーにリクエストを送信する際、フィルターはリクエストをインターセプトし、文字エンコーディングの設定、権限の検証、ログ記録などの特殊な機能を完了することができます。

  • フィルターの実行フロー

11.2 リスナー

  • とは何か?

リスナーは、ServletContext、セッション、requestの3つのオブジェクトの作成、破棄、またはその属性への追加、変更、削除が行われるときに自動的にコードを実行する機能コンポーネントです。

  • リスナーの分類:JavaWebは8つのリスナー(インターフェース)を提供しています

タグ: Java サーブレット Web開発 HTTP JavaEE

6月18日 20:31 投稿