記事ディレクトリ
1. まずサーブレットについて理解する
1.1 サーブレットとは
サーブレットは Java で書かれたサーバー側プログラムで、クライアント (通常はブラウザ) から送信されたリクエストを処理し、クライアントに応答を返すために使用されます。これは Java EE 仕様の一部であり、サーバー上でコンテンツを動的に生成する方法を提供します。
サーブレットは Java に基づいて実装されているため、クロスプラットフォームであり、さまざまなオペレーティング システム上で実行できます。Javaの安全性と信頼性を踏襲しており、オブジェクト指向プログラミングや豊富なクラスライブラリなどJava言語の利点も最大限に活用できます。
サーブレットは主に、Web サイト、Web サービス、Web アプリケーション バックエンドなどの Web アプリケーションを開発するために使用されます。Tomcat コンテナがリクエストを受信すると、それを対応するサーブレット クラスに渡して処理します。同時に、サーブレットはリクエスト パラメータ、セッション状態、データベース クエリなどに従って HTML、XML、JSON、およびその他のコンテンツを動的に生成し、クライアントに応答を送信できます。
全体として、サーブレットは動的なページを実現するためのテクノロジーであり、開発者が Web プログラムを簡単かつ効率的に開発できるように Tomcat が開発者に提供する API のセットです。
1.2 サーブレットの主な働き
サーブレットは、Java Web 開発において主に次のタスクを担当します。
-
サーブレット クラスを登録し、HTTP リクエストを処理します。
- サーブレットを使用すると、プログラマは Java クラスを登録し、Tomcat (または他の Java サーブレット コンテナ) が特定の HTTP リクエストを受信したときにそのクラス内のコードを実行できます。
web.xml
サーブレットを特定のサーブレットに関連付けるURL
か、ファイル内でサーブレット マッピングを構成するか、アノテーションを使用しますURL
。 - クライアントが対応する HTTP リクエストを送信すると、サーブレット コンテナはインスタンスを作成し、対応するサーブレットを呼び出してリクエストを処理します。開発者は、サーブレットでビジネス ロジックを作成し、リクエストからパラメータを取得し、データベース操作を実行し、動的コンテンツを生成し、クライアントに返す HTTP 応答を生成できます。
- サーブレットを使用すると、プログラマは Java クラスを登録し、Tomcat (または他の Java サーブレット コンテナ) が特定の HTTP リクエストを受信したときにそのクラス内のコードを実行できます。
-
HTTP リクエストを解析します。
- サーブレットは、プログラマが HTTP リクエストを解析し、クライアントによって送信された HTTP リクエストを文字列からオブジェクトに解析するのに役立ちます
HttpRequest
。HttpRequest
このオブジェクトはさまざまなメソッドを提供し、開発者が HTTP リクエスト メソッド (GET、POST など)、リクエスト ヘッダー、リクエスト パラメータ、セッション情報などを簡単に取得できるようにします。
- サーブレットは、プログラマが HTTP リクエストを解析し、クライアントによって送信された HTTP リクエストを文字列からオブジェクトに解析するのに役立ちます
-
HTTP 応答を作成します。
- サーブレットは、プログラマが HTTP 応答を構築するのに役立ちます。そのため、開発者は
HttpResponse
指定されたオブジェクトの属性フィールドを設定するだけで済みます。サーブレットは、HTTP プロトコルに従って HTTP 応答文字列を自動的に構築し、それをソケット経由でクライアントに書き戻します。 HttpResponse
このオブジェクトは、応答ステータス コード、応答ヘッダー、応答内容などを設定するためのメソッドを提供します。開発者はビジネス ニーズに応じて設定でき、サーブレットは設定された応答情報をクライアントに返します。
- サーブレットは、プログラマが HTTP 応答を構築するのに役立ちます。そのため、開発者は
一般に、サーブレットは Java サーバー側テクノロジとして、開発者による HTTP リクエストとレスポンスの処理を支援し、特定の Java クラスの登録と実行を可能にし、HTTP リクエストを解析し、設定されたオブジェクトに基づいて HTTP レスポンスを構築しますHttpResponse
。これにより、Java 開発者は動的で対話型の Web アプリケーションを簡単に開発できるようになります。
1.3 サーブレットと Tomcat の関係
Servlet と Tomcat の間には密接な関係があり、切っても切り離せない組み合わせとも言えますが、両者の関係を以下に説明します。
- Tomcat はサーブレットのコンテナであり、サーブレットが Tomcat サーバー上で実行できるように、サーブレットの実行環境とサポートを提供します。
- Web アプリケーションが Tomcat にデプロイされると、Tomcat はサーブレット オブジェクトのロードと初期化を担当し、HTTP リクエストを受信すると、対応するサーブレット オブジェクトを呼び出してリクエストを処理し、応答を生成します。
- Tomcat は Web アプリケーションを管理および監視する機能も提供しており、Tomcat の管理インターフェイスを通じてデプロイされたサーブレットおよび Web アプリケーションを管理できます。
要約すると、Servlet は Java で書かれたサーバー側プログラムであり、Tomcat は Servlet を実行および管理できるコンテナーです。これらが連携して Java Web アプリケーションのインフラストラクチャを形成し、開発者が動的で対話型の Web アプリケーションを構築できるようになります。
2 番目、最初のサーブレット プログラム
2.1 Maven プロジェクトの作成
メイブンとは:
Maven プロジェクトは、Apache Maven ビルド ツールに基づく Java プロジェクトです。Maven は、Java プロジェクトのビルド プロセスを簡素化し、標準化するために設計された、人気のあるプロジェクト管理およびビルド ツールです。Maven を使用すると、開発者はプロジェクトの依存関係の管理、プロジェクトの構築、単体テストの実行、プロジェクト ドキュメントの生成などをより簡単に行うことができます。
典型的な Maven プロジェクトには通常、次の重要な部分が含まれています。
-
pom,xml
ファイル:pom.xml
Maven プロジェクトのコア構成ファイルであり、プロジェクトのルート ディレクトリにあります。pom.xml
このファイルは、プロジェクトのメタデータ情報 (プロジェクトの名前、バージョン、説明など) を記述し、プロジェクトの依存関係 (必要なサードパーティのライブラリとプラグイン) を定義し、プロジェクトのビルド プロセス (コンパイル、パッケージ化) を構成するために使用されます。 、など)、およびその他のプロジェクト関連の構成。 -
src
ディレクトリ: このディレクトリは、プロジェクトのソース コードとリソース ファイルのルート ディレクトリです。通常、次の 2 つのサブディレクトリが含まれます。src/main
: メインのソース コードとリソース ファイルが含まれます。src/test
: 単体テストのコードとリソースが含まれます。
-
target
Directory : このディレクトリは、Maven ビルドによって生成される出力ディレクトリであり、コンパイルされたクラス ファイル、パッケージ化されたファイル、ドキュメントなど、ビルドされたプロジェクトによって生成される出力ファイルが含まれます。JAR
WAR
-
Maven のライフサイクルとプラグイン: Maven は、ビルド プロセス中にさまざまなタスクを実行するためのライフサイクルとプラグインの標準セットを定義します。開発者は、
pom.xml
コンパイル、テスト、パッケージ化、展開などの構成ファイルを通じて、さまざまな構築段階でプロジェクトによって実行されるタスクを定義できます。
IDEA を使用して Maven プロジェクトを作成します。
- IDEA で新しいプロジェクトを作成し、
Build System
オプションで Maven を選択し、中国語やその他の特殊な記号が含まれていないプロジェクト パスに注意してください。
- 次のインターフェイスが表示され、作成が成功したことが示されます。
2.2 依存関係の導入
Maven プロジェクトが作成されるとpom.xml
ファイルが自動的に生成されるので、pom.xml
そのファイル内にサーブレット API が依存するパッケージを導入する必要がありますJAR
。
- 中央ウェアハウス(https://mvnrepository.com)を検索すると
servlet
、通常、最初の結果が必要な API になります。
2. バージョンを選択します。ここでは3.1.0
バージョンを選択します。
Servlet と Tomcat のバージョンの一致について:
- バージョンの対応はhttps://tomcat.apache.org/thatversion.htmlで確認できます。
- Tomcat のバージョンは 8.5 なので、Servlet API 3.1.0 を使用することにしました。
- 中央リポジトリから提供された
Maven xml
ものをコピーしてpom.xml
ファイルに貼り付けます
新しいdependencies
ラベルを追加するには、それをこのラベルにコピーすることに注意してください。
2.3 ディレクトリの作成
Maven プロジェクトを作成した後、IDEA は次のディレクトリ構造を自動的に生成します
。
src
ソースコードが配置されているディレクトリを示します。main/java
ソース コードのルート ディレクトリを示します。その後作成される.java
ファイルはこのディレクトリに配置されます。main/resources
プロジェクトのいくつかのリソース ファイルが配置されているディレクトリを示します。test/java
テストコードのルートディレクトリを示します。
有了这些目录还不够,我们还需要创建以下新的目录和文件。
1.webapp
ディレクトリの作成
main
このディレクトリの下に、java
ディレクトリ と並行してwebapp
ディレクトリを作成します。このディレクトリは、HTML ページ、JSP ファイル、CSS スタイル、JavaScript スクリプトなどの Web アプリケーションの Web リソースを保存するために使用されます。
2.WEB-INF
ディレクトリの
作成 ディレクトリの下にwebapp
、WEB-INF
という名前のディレクトリを作成します。このディレクトリは、Web アプリケーションの構成と保護されたリソースを保持する Web アプリケーション内の特別なディレクトリです。
3.web.xml
ファイルを作成します
WEB-INF
このディレクトリ内に、web.xml
という名前のファイルを作成します。これは Java Web アプリケーションのデプロイメント記述子であり、サーブレット、フィルター、リスナーなどの Web コンポーネント、およびその他のアプリケーション関連の構成情報を構成するために使用されます。
4.web.xml
文書を書く
web.xml
次のコードをファイルにコピーします。
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
</web-app>
2.4 コードの記述
次のコードを使用して、ディレクトリにクラスjava
を作成します。HelloServlet
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// super.doGet(req, resp);
System.out.println("hello world");
resp.getWriter().write("hello world");
}
}
このコードの説明:
- ;を継承
HttpServlet
するクラスを作成します。HelloServlet
- このクラスの上に追加されたアノテーションは
@WebServlet("/hello")
、サーブレットURL
マッピングを指定するために使用されます。この例では、サーブレットは のURL
パスにマップされ、/hello
クライアントがへの/hello
HTTP リクエストを開始するとHelloServlet
呼び出されます。 doGet
オーバーライドされたメソッドにはdoGet
2 つのパラメーターがあり、それぞれ受信した HTTP 要求と構築される HTTP 応答を表します。このメソッドは、GET
Tomcat がリクエストを受信すると、Tomcat によって自動的に呼び出されるようにトリガーされます。HttpServletRequest
HTTP リクエストを表します。Tomcat は、文字列形式のリクエストをHTTP リクエストの形式に従ってHttpServletRequest
オブジェクトに変換します。リクエスト内の後続の情報 (メソッド、url
、header
などbody
) は、このオブジェクトを通じて取得されます。HttpServletResponse
HTTP 応答を表すには、プログラマはコード内でこのオブジェクトを構築する必要があります(応答のステータス コードの構築などheader
)body
。resp.getWriter()
文字ストリーム オブジェクトが取得されWriter
、このストリーム オブジェクトを通じて一部のデータを書き込むことができます。書き込まれたデータは HTTP 応答の一部として構築されbody
、Tomcat は応答全体を文字列に変換してsocket
クライアントに書き戻します。を通して 。
これらの数行のコードは少ないですが、多くの情報が含まれています。
- サーブレット プログラムのコードでは、
main
プログラムのエントリ ポイントとしてメソッドを記述する必要はありません。main
メソッドは既に Tomcat に含まれており、記述したコードは必要に応じて Tomcat によって呼び出されるためです。- ここで記述するコードは完全なプログラムではなく、Tomcat プログラムのロジックの一部に過ぎませんが、Tomcat から適切なタイミングで正しく呼び出されるようにするには、次の 3 つの条件を満たす必要があります。クラス
からHttpServlet
継承する必要がある; 2) このクラスはアノテーションを使用して HTTP パスを関連付ける
必要がある;@WebServlet
3) このクラスはdoXXX
メソッドを実装する必要がある。
2.5 パッケージャ
- IDEA でのパッケージ化には Maven を使用できます。通常、IDEA の右側に Maven ウィンドウが表示されます。表示されない場合は、 で開くことができます
View -> View -> Tool Window -> Maven
。
- 展開し
Lifecycle
てpackage
ダブルクリックしてパッケージ化します
次の情報が表示されたら、パッケージ化が成功したことを意味します。
target
パッケージ化が成功すると、次のディレクトリにJAR
パッケージが生成されていることがわかります。
ただし、そのようなJAR
パッケージは私たちが必要とするものではなく、Tomcat が認識する必要があるのは別のWAR
形式のパッケージです。このパッケージの名前は複雑すぎるため、シンプルな名前を付けたいと考えています。
pom.xml
構成ファイルを変更してWAR
パッケージを生成し、WAR
パッケージ名を変更します
pom.xml
に新しいタグを追加して、packing
パッケージ化方法がWAR
パッケージをパッケージ化することを示します。- 別の
build
タグと組み込みタグを追加して、出力パッケージfinalName
の名前が であることを示します。WAR
hello_servlet
- パッケージ化に Maven を再利用する
2.6 導入手順
上記のパッケージ化によって形成されたWAR
パッケージ ファイルをwebapps
Tomcat のディレクトリにコピーします。Tomcat
を実行すると、自動的に解凍され、この時点でデプロイメントが完了します。
2.7 検証手順
この時点で、ブラウザ経由でアクセスするとhttp://127.0.0.1:8080/hello_servlet/hello
、次の結果が表示されます。
hello world
この時点で、最初のサーブレット プログラムが完成しましたが、これを見れば、これがこれまでで最も学習が面倒なプログラムであると確信するでしょう。
3. Smart Tomcat 導入プログラム
手動でパッケージ化して Tomcat にコピーする上記のプロセスは非常に面倒に思えますが、IDEA はプログラムをパッケージ化してデプロイするためのより便利なプラグインを提供します。「Smart Tomcat」プラグインは、IntelliJ IDEA での Apache Tomcat の使用エクスペリエンスを簡素化し、向上させるために開発されました。「Smart Tomcat」プラグインを使用すると、Tomcat サーバーの設定と管理がより便利になり、Java Web アプリケーションのデプロイとデバッグが簡単になります。
3.1 Smart Tomcat プラグインのインストール
IDEA: で開きFile -> Settings -> plugins
、Smart Tomcat を検索し、指示に従って IDEA を再起動します。
3.2 Smart Tomcat プラグインの構成
- IDEA の右上隅にある「現在のファイル」をクリックします。
- 次に、「構成の編集」をクリックします。
- 「新しい実行構成を追加」をクリックします。
- 「スマート Tomcat」を選択します
- 次に、次のように設定します
- 構成が完了すると、最初の「現在のファイル」の位置が「実行」に変更されていることがわかり、その横にある緑色の三角形をクリックすると、Tomcat が自動的に実行され、プログラムがデプロイされます。
- 走る
この時点ではIDEAのコンソールにTomcatのログが出力され、文字化けはありません。
- アクセスサーバー
4. アクセスエラーの原因
4.1 404 が表示される
404 は、ユーザーがアクセスしたリソースが存在せず、パスがURL
正しく書かれていない可能性が高いことを示します。
エラー例1:「コンテキストパス」が省略され、「/hello」でサーバーに直接アクセスする
エラー例2:「サーブレットパス」が省略され、「/hello_servlet」経由でサーバーに直接アクセスする
4.2 405が登場
405 は、対応する HTTP リクエスト メソッドが実装されていないことを意味します。
たとえば、クラス内の
HelloServlet
オーバーライドされたメソッドをコメントアウトし、Tomcat を再起動してサーバーに再度アクセスします。doGet
4.3 500が登場
500 の理由は、多くの場合、サーブレット コードでスローされた例外によって発生します。
たとえば、次のようにメソッドを変更し
HelloServlet
、doGet
Tomcat を再起動してサーバーにアクセスします。
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String s = null;
resp.getWriter().write(s);
}
このとき、サーバーに内部エラーがあることを示す null ポインタ例外が発生するため、返される応答コードは 500 です。
4.4 「このWebサイトにアクセスできません」と表示される
この状況は通常、Tomcat が起動していないか、Tomcat サーバーの起動に失敗したことを意味します。
たとえば、「サーブレット パス」が正しく記述されていない場合、Tomcat サーバーの起動に失敗します。
この時点で、Tomcat を開始すると、エラーが発生します。
サーバーにアクセスすると「このウェブサイトにアクセスできません」と表示される
5、サーブレットの動作原理とライフサイクル
5.1 動作原理
サーブレットは、Web リクエストとレスポンスを処理する Java のコンポーネントです。これは Web サーバー内で実行され、Apache Tomcat などの Web コンテナーを通じてそのライフサイクルとリクエストのスケジュールを管理します。
サーブレット操作の主な原則は次のとおりです。
-
Web コンテナの初期化: Web サーバーが起動すると、Web コンテナはサーブレットをロードして初期化します。初期化中に、Web コンテナはサーブレット インスタンスを作成し、その
init()
メソッドを呼び出します。このメソッドでは、サーブレットは、構成情報のロード、データベース接続の確立など、いくつかの初期化操作を実行できます。 -
HTTP リクエストの受信:ユーザーがブラウザで
URL
Web HTTP` リクエストにアクセスしたとき服务器接收到
。 -
リクエストの分散: Web コンテナは、リクエスト
URL
とデプロイメント記述子 () 内の構成情報に基づいてweb.xml
、どのサーブレットがリクエストを処理するかを決定します。 -
サーブレットの
service()
メソッドの呼び出し: どのサーブレットがリクエストを処理するかを決定すると、Web コンテナはサーブレット インスタンスでメソッドを呼び出しservice()
、リクエスト オブジェクトと応答オブジェクトをパラメータとして渡します。 -
サーブレット処理リクエスト:
service()
このメソッドでは、サーブレットは HTTP リクエスト タイプ (GET、POST、PUT など) に従って対応するロジックを実行し、リクエストを処理して応答を生成します。 -
HTTP 応答を返す: リクエストの処理後、サーブレットは生成された HTTP 応答を応答オブジェクトに書き込みます。
-
Destroy : Web サーバーがシャットダウンされるか、サーブレットが不要になると、Web コンテナはサーブレットの
destroy()
メソッドを呼び出し、一部のリソースを解放してクリーンアップする機会を与えます。
5.2 ライフサイクル
サーブレットのライフサイクルには主に次の 3 つの段階があります。
-
初期化(初期化):
- サーブレット コンテナが起動すると、デプロイメント記述子 (またはアノテーション設定) を読み取り
web.xml
、サーブレット クラスをロードします。 - サーブレット インスタンスを作成し、その
init(ServletConfig config)
メソッドを呼び出して初期化します。 init()
このメソッドは、サーブレットが初めてロードされるときに 1 回だけ呼び出され、構成情報のロード、データベース接続の確立などの一部の初期化操作を実行するために使用されます。
- サーブレット コンテナが起動すると、デプロイメント記述子 (またはアノテーション設定) を読み取り
-
リクエストの処理:
- サーブレットが初期化されると、サービス可能な状態になり、クライアントのリクエストを処理できるようになります。
- クライアントがサーバーにリクエストを送信し、そのリクエストを処理するサーブレットと一致すると、サーブレット コンテナは同じサーブレット インスタンス上のメソッドを呼び出します
service(ServletRequest request, ServletResponse response)
。 service()
このメソッドは、リクエストの種類 (GET、POST、PUT など) に応じて、対応するdoXXX()
メソッド (たとえばdoGet()
、 、doPost()
など) を呼び出してdoPut()
リクエストを処理します。
-
破壊:
- サーブレット コンテナが閉じられるか、サーブレットが不要になると、サーブレット インスタンスは破棄されます。
destroy()
サーブレット コンテナは破棄される前に、サーブレットのメソッドを呼び出して、リソースの解放とクリーンアップの操作を実行します。destroy()
このメソッドは、サーブレットがアンロードされる前に 1 回だけ呼び出され、データベース接続のクローズやリソースの解放などの後続作業を実行するために使用されます。
サーブレットのライフサイクル全体を次の図に示します。
知らせ:
- サーブレット コンテナはサーブレットのライフ サイクルを管理する役割を果たし、開発者はサーブレット インスタンスを手動で作成および破棄する必要がありません。
- サーブレットは存続期間中に何度もインスタンス化される可能性がありますが、
init()
メソッドとdestroy()
メソッドは 1 回しか呼び出されません。 - リクエストの処理中、各リクエストは別のスレッドでメソッドを呼び出します
service()
。また、複数のリクエストが同じサーブレット インスタンスに同時にアクセスできるため、開発者はサーブレットのスレッド セーフを確保する必要があります。
6. サーブレットAPI
サーブレット API は、Java サーブレット プログラムの開発に使用されるインターフェイスとクラスのコレクションです。これは、Web リクエストとレスポンスを処理するための標準化された方法を提供し、開発者が Java ベースの Web アプリケーションを作成できるようにします。
サーブレット API は Java EE 仕様に含まれており、開発者は Java サーブレット仕様を通じてこれらのインターフェイスとクラスの詳細を学ぶことができます。Java では、サーブレット API は通常、javax.servlet
パッケージと、javax.servlet.http
そのパッケージ内のクラスおよびインターフェースで構成されます。
以下は、サーブレット API のいくつかのコア クラスとインターフェイス、およびそれらの簡単な説明です。
クラス/インターフェース | 説明する |
---|---|
javax.servlet.Servlet |
サーブレット インターフェイス。すべてのサーブレット クラスはこのインターフェイスを実装する必要があります。 |
javax.servlet.GenericServlet |
サーブレット抽象クラスは、サーブレット インターフェイスのほとんどのメソッドを実装します。 |
javax.servlet.http.HttpServlet |
HttpServlet クラスは、HTTP 関連の要求と応答を処理するために使用されます。 |
javax.servlet.ServletConfig |
サーブレットの初期化パラメータを取得するために使用されるインターフェイス。 |
javax.servlet.ServletContext |
Web アプリケーション全体で情報を共有するためのインターフェイス。 |
javax.servlet.ServletException |
例外クラス。サーブレットによってスローされる可能性のある例外の基本クラス。 |
javax.servlet.ServletRequest |
クライアントによって要求されたオブジェクトを表すために使用されるインターフェイス。 |
javax.servlet.ServletResponse |
サーブレットの応答オブジェクトを表すために使用されるインターフェイス。 |
javax.servlet.http.HttpServletRequest |
ServletRequest から拡張された、HTTP リクエストを表すインターフェース。 |
javax.servlet.http.HttpServletResponse |
ServletResponse から拡張された、HTTP 応答を表すインターフェイス。 |
javax.servlet.http.HttpSession |
クライアントとサーバーの間で状態を維持するためのインターフェイス。 |
javax.servlet.RequestDispatcher |
リクエストを他のリソース (サーブレット、JSP など) に転送するためのインターフェイス。 |
javax.servlet.Filter |
リクエストとレスポンスの間でフィルタリング操作を実行するためのフィルタ インターフェイス。 |
javax.servlet.FilterChain |
複数のフィルター間でリクエストとレスポンスを渡すためのフィルター チェーン。 |
6.1 HttpServlet クラス
HttpServlet
Java サーブレット API の抽象クラスであり、GenericServlet
クラスを拡張し、HTTP プロトコルに基づいてリクエストとレスポンスを処理するためのより便利なメソッドを提供します。HttpServlet
HTTP ベースのサーブレットを作成するための共通の基本クラスです。
以下はHttpServlet
クラス内で一般的に使用されるメソッドです。
方法 | 説明する |
---|---|
void init(ServletConfig config) |
サーブレットを初期化するためにサーブレット インスタンスが作成された直後に呼び出されます。 |
void destroy() |
サーブレット コンテナが通常にシャットダウンしてリソースを解放し、操作をクリーンアップするときに呼び出されます。 |
void doGet(HttpServletRequest request, HttpServletResponse response) |
HTTP GET リクエストを処理します。 |
void doPost(HttpServletRequest request, HttpServletResponse response) |
HTTP POST リクエストを処理します。 |
void doPut(HttpServletRequest request, HttpServletResponse response) |
HTTP PUT リクエストを処理します。 |
void doDelete(HttpServletRequest request, HttpServletResponse response) |
HTTP DELETE リクエストを処理します。 |
void service(HttpServletRequest request, HttpServletResponse response) |
リクエストのHTTPメソッドに応じて、対応するdoXXXメソッドを呼び出して処理します。 |
ServletConfig getServletConfig() |
現在のサーブレットの構成情報を取得します。 |
String getServletInfo() |
サーブレットの説明情報を取得します。 |
String getInitParameter(String name) |
指定された名前の初期化パラメータ値を取得します。 |
Enumeration<String> getInitParameterNames() |
すべての初期化パラメータ名の列挙を取得します。 |
ServletContext getServletContext() |
サーブレット環境の情報にアクセスするために使用されるサーブレット コンテキスト オブジェクトを取得します。 |
String getServletName() |
サーブレットの名前を取得します。 |
void log(String msg) |
メッセージをサーブレット コンテナのログ システムに記録します。 |
void log(String msg, Throwable throwable) |
例外情報をサーブレット コンテナのログ システムに記録します。 |
void setInitParameter(String name, String value) |
初期化パラメータの値を設定します。 |
boolean isAsyncSupported() |
サーブレットが非同期処理をサポートしているかどうかを確認します。 |
void startAsync() |
非同期処理を開始します。 |
void startAsync(ServletRequest request, ServletResponse response) |
指定されたリクエストとレスポンスで非同期処理を開始します。 |
HttpServlet
このクラスの重要な特徴をいくつか示します。
-
継承関係:
HttpServlet
から継承されているGenericServlet
ため、GenericServlet
と共通の機能がいくつかありますが、HTTP リクエストと応答の処理に特化しています。 -
HTTP リクエスト処理メソッドを提供する:
HttpServlet
さまざまな HTTP リクエスト メソッド (GET、POST、PUT、DELETE など) の処理メソッドを提供します。これらのメソッドは、特定のリクエスト タイプを処理するためにサブクラスによって書き換えることができます。 -
默认实现:
HttpServlet
中这些 HTTP 请求处理方法默认实现是返回 HTTP 状态码 405(Method Not Allowed),表示不支持对应的请求方法。因此,子类需要根据需要重写这些方法以提供自定义的请求处理逻辑。 -
获取请求信息:
HttpServletRequest
参数提供了访问客户端 HTTP 请求信息的方法,如获取请求 URL、请求参数、请求头、会话信息等。 -
设置响应信息:
HttpServletResponse
参数提供了设置 HTTP 响应内容、响应状态码、响应头等的方法。
由于 HttpServlet
是一个抽象类,不能直接实例化,开发者需要编写具体的子类来继承 HttpServlet
,然后实现相应的请求处理方法,根据业务逻辑处理不同类型的 HTTP 请求,并生成相应的 HTTP 响应。
6.2 HttpServletRequest 接口
HttpServletRequest
是Java Servlet API 中的一个接口,用于表示客户端发起的 HTTP 请求。它提供了访问客户端请求信息的方法,开发者可以通过该接口获取请求的各种属性、参数、头信息等。该接口继承自 ServletRequest
接口,专门用于处理 HTTP 请求。
6.2.1 核心方法
以下是一些常用的 HttpServletRequest
接口的方法:
1. 获取请求信息
方法 | 说明 |
---|---|
String getMethod() |
获取 HTTP 请求方法,例如 GET、POST、PUT 等。 |
String getRequestURI() |
获取请求的 URI(Uniform Resource Identifier)部分,即去除协议、主机、端口号后的路径部分。 |
String getQueryString() |
获取请求的查询字符串,即 URL 中 “?” 后面的部分。 |
String getProtocol() |
获取请求使用的协议,如 “HTTP/1.1”。 |
String getContextPath() |
获取应用程序的上下文路径(Context Path),即应用程序的根路径。 |
2. 获取请求参数
方法 | 说明 |
---|---|
String getParameter(String name) |
获取指定名称的请求参数值。 |
Enumeration<String> getParameterNames() |
获取所有请求参数名称的枚举。 |
String[] getParameterValues(String name) |
获取指定名称的请求参数值数组,用于处理同名参数。 |
3. 获取请求头信息
方法 | 说明 |
---|---|
String getHeader(String name) |
获取指定名称的请求头信息。 |
Enumeration<String> getHeaderNames() |
获取所有请求头名称的枚举。 |
4. 获取会话信息
方法 | 说明 |
---|---|
HttpSession getSession() |
获取与该请求关联的 HttpSession 对象,如果没有会话,则创建一个新的会话。 |
HttpSession getSession(boolean create) |
根据指定的 create 参数决定是否创建新的会话。true 代表创建,false 代表不创建。 |
5. 获取客户端信息
方法 | 说明 |
---|---|
String getRemoteAddr() |
获取客户端的 IP 地址。 |
String getRemoteHost() |
获取客户端的主机名。 |
int getRemotePort() |
获取客户端连接的端口号。 |
6. 其他常用方法
方法 | 说明 |
---|---|
Cookie[] getCookies() |
获取客户端发送的所有 Cookie。 |
Locale getLocale() |
获取客户端的首选语言环境。 |
Enumeration<Locale> getLocales() |
获取客户端支持的所有语言环境的枚举。 |
通过使用 HttpServletRequest
接口提供的这些方法,开发者可以在 Servlet 中获取请求的各种信息,从而更好地处理和响应客户端的请求。
6.2.2 获取请求信息
创建一个 ShowHeaderServlet
类, 用于获取请求信息并返回响应。它可以响应 HTTP GET 请求,并将请求的一些信息以及请求头信息返回到客户端。
@WebServlet("/show")
public class ShowHeaderServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=utf8");
StringBuilder respBody = new StringBuilder();
respBody.append(req.getProtocol());
respBody.append("<br>");
respBody.append(req.getMethod());
respBody.append("<br>");
respBody.append(req.getRequestURL());
respBody.append("<br>");
respBody.append(req.getContextPath());
respBody.append("<br>");
respBody.append(req.getQueryString());
respBody.append("<br>");
respBody.append("<h3> headers: <h3>");
Enumeration<String> headerNames = req.getHeaderNames();
while (headerNames.hasMoreElements()){
String headerName = headerNames.nextElement();
respBody.append(headerName).append(": ").append(req.getHeader(headerName));
respBody.append("<br>");
}
resp.getWriter().write(respBody.toString());
}
}
访问服务器,可以发现响应内容如下:
6.2.3 获取 GET 请求中的参数
创建一个 GetParameterServlet
类用于获取 GET 请求中的参数。
GET 方法的查询字符串说明:
- 在 HTTP 请求中,GET 方法通过将参数附加到 URL 的查询字符串(Query String)部分来传递数据。查询字符串是在 URL 的问号(?)后面的部分,用于向服务器传递键值对参数。它的格式通常为:
http://example.com/path/to/resource?key1=value1&key2=value2&key3=value3...
代码如下:
@WebServlet("/getParameter")
public class GetParameterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 期待收到的请求:http://localhost:8080/hello_servlet/getParameter?studentId=10&classId=20
resp.setContentType("text/html; charset=utf8");
String studentId = req.getParameter("studentId");
String classId = req.getParameter("classId");
resp.getWriter().write("studentId: " + studentId + ", classId: " + classId);
}
}
访问服务器结果:
6.2.4 通过 form 表单获取 POST 请求中的参数
1. 首先约定前后端的交互接口
POST /postParameter
2. 前端创建页面testPost.html
,里面创建一个 form
表单
<form action="postParameter" method="post">
<input type="text" name="studentId">
<input type="text" name="classId">
<input type="submit" value="提交">
</form>
3. 后端创建一个 PostParameterServlet
类,用于接收 form
表单提交的数据
@WebServlet("/postParameter")
public class PostParameterServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html; charset=utf8");
String studentId = req.getParameter("studentId");
String classId = req.getParameter("classId");
resp.getWriter().write("studentId: " + studentId + ", classId: " + classId);
}
}
4. 访问服务器页面,然后提交参数
5. 提交数据后会自动跳转至响应页面
6. 通过 Fiddler
工具抓包,可以发现通过 POST
方法传递的参数在 body
中
6.2.5 通过 JSON 获取 POST 请求中的参数
1. 约定前后端交互接口
POST /jsonPostParameter
2. 后端获取 body
并通过 Jackson
解析 JSON 字符串
在Java代码中,需要引入
Jackson
这个库,完成对 JSON 的解析工作:
- 在中央仓库中搜索
jackson
,选择 “JackSon Databind”:
- 把中央仓库中的依赖拷贝到
pom.xml
文件中,形如:
创建一个JsonPostServlet
类:
class Student {
public String studentId;
public String classId;
}
@WebServlet("/jsonPostParameter")
public class JsonPostParameterServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ObjectMapper objectMapper = new ObjectMapper();
Student student = objectMapper.readValue(req.getInputStream(), Student.class);
resp.getWriter().write("studentId: " + student.studentId + ", classId: " + student.classId);
}
}
其中,Student
类用于储存 JSON 解析出来的数据。
3. 启动服务器,然后通过 Postman 工具发送 JSON 格式的数据
4. 服务器响应内容
6.3 HttpServletResponse 接口
HttpServletResponse
也是 Java Servlet API 中的一个接口,它表示用于发送 HTTP 响应给客户端的对象。通过这个接口,Servlet 可以设置响应的内容、响应头、状态码等,以便向客户端发送适当的响应。
6.3.1 核心方法
下面是 HttpServletResponse
接口的核心方法及其说明:
1. 设置响应头和状态码
方法 | 说明 |
---|---|
void setStatus(int sc) |
设置响应的状态码。 |
void setStatus(int sc, String sm) |
设置响应的状态码和描述信息。 |
int getStatus() |
获取响应的状态码。 |
String getHeader(String name) |
获取指定名称的响应头的值。 |
Collection<String> getHeaderNames() |
获取所有响应头名称的集合。 |
Collection<String> getHeaders(String name) |
获取指定名称的响应头的所有值的集合。 |
void setHeader(String name, String value) |
设置指定名称的响应头的值。 |
void addHeader(String name, String value) |
添加指定名称的响应头的值(可以多次调用)。 |
void setIntHeader(String name, int value) |
设置指定名称的整数响应头的值。 |
void addIntHeader(String name, int value) |
添加指定名称的整数响应头的值(可以多次调用)。 |
2. 设置响应内容
方法 | 说明 |
---|---|
PrintWriter getWriter() |
获取用于发送字符文本的 PrintWriter 对象。 |
ServletOutputStream getOutputStream() |
获取用于发送二进制数据的 ServletOutputStream 对象。 |
void setContentLength(int len) |
设置响应内容的长度。 |
void setContentType(String type) |
设置响应内容的 MIME 类型。 |
String getContentType() |
获取响应内容的 MIME 类型。 |
void setCharacterEncoding(String charset) |
设置响应内容的字符编码。 |
3. 缓存控制
方法 | 说明 |
---|---|
void setDateHeader(String name, long date) |
设置指定名称的日期型响应头的值。 |
void addDateHeader(String name, long date) |
添加指定名称的日期型响应头的值(可以多次调用)。 |
void setExpires(long expires) |
设置响应的过期时间。 |
void addCookie(Cookie cookie) |
添加一个 Cookie 到响应中。 |
4. 重定向和错误处理
方法 | 说明 |
---|---|
void sendRedirect(String location) |
重定向到另一个 URL 。 |
void sendError(int sc) |
发送一个错误状态码。 |
void sendError(int sc, String msg) |
发送一个错误状态码和描述信息。 |
void reset() |
重置响应对象,清除所有设置的响应数据。 |
5. 其他方法
方法 | 说明 |
---|---|
void setBufferSize(int size) |
设置响应缓冲区的大小。 |
int getBufferSize() |
获取响应缓冲区的大小。 |
void flushBuffer() |
强制将缓冲区中的数据发送到客户端。 |
boolean isCommitted() |
检查是否已经提交响应。 |
void resetBuffer() |
重置响应缓冲区。 |
void setLocale(Locale loc) |
设置响应内容的区域设置。 |
这些方法提供了处理 Servlet 响应的各种操作和设置,开发人员可以根据需要使用这些方法来构建定制化的 HTTP 响应。
6.3.2 设置状态码
创建一个 StutasServlet
类,用于接收一个状态码参数,然后在响应中设置该状态码:
@WebServlet("/status")
public class StatusServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 期待收到请求:http://localhost:8080/hello_servlet/status?status=200
String statusStr = req.getParameter("status");
int status = Integer.parseInt(statusStr);
resp.setStatus(status);
resp.getWriter().write("status: " + status);
}
}
通过 Fiddler
工具抓包查看设置的响应状态码:
6.3.3 设置自动刷新
创建一个 AutoRefreshServlet
类,当浏览器访问时会每隔一秒自动刷新一次页面,并显示当前的时间戳。
@WebServlet("/autoRefresh")
public class AutoRefreshServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("refresh", String.valueOf(1));
Timestamp timestamp = new Timestamp(System.currentTimeMillis());
resp.getWriter().write("timestamp: " + timestamp.toString());
}
}
其中,通过 HTTP 响应报头中的 Refresh
字段,可以控制浏览器自动刷新的时机。
6.3.4 设置重定向
创建一个RedirectServlet
类,返回一个重定向 HTTP 请求,自动跳转到另一个页面。
@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.sendRedirect("https://csdn.net");
}
}
启动程序,通过 URL: http://localhost:8080/hello_servlet/redirect
访问,可以看到页面自动跳转到 CSDN 主页了。
通过 Fiddler
抓包可以看到,响应设置了 302 重定向状态码,重定向的位置就是 https://csdn.net
:
七、Cookie 和 Session
7.1 什么是 Cookie 和 Session
Cookie:
- Cookie 是一种在客户端(浏览器)存储小型数据的机制,以便在客户端和服务器之间传递数据。
- 服务器可以在 HTTP 响应头中通过
Set-Cookie
标头发送一个或多个 Cookie 到客户端,客户端会将这些 Cookie 存储在本地。 - 之后,当客户端再次请求同一个服务器,它会将之前存储的 Cookie 自动包含在请求头中的
Cookie
标头内,从而将相关数据传递给服务器。
Session:
- Session 是一种服务器端存储数据的机制,用于跟踪用户在网站上的状态和活动。
- 当用户首次访问网站时,服务器会为每个用户创建一个
唯一的会话标识
(通常是一个长随机字符串),并将其存储在 Cookie 中,发送到客户端。 - 之后,客户端的每个请求都会自动包含会话标识,服务器通过会话标识识别用户,并在服务器端的会话存储中保存用户的状态和数据。
7.2 深入理解会话机制
服务器在同一时刻一般都会收到很多请求,因此就需要区分清楚哪一个请求是属于哪一个用户(客户端)的,所有就要求服务器与每一个用户都维持一个会话
,并且这个会话中包含了这个用户的唯一标识以及与用户信息的对应关系。
因此,会话的本质就是一个 “哈希表”,其中的key就是对用户的唯一标识(sessionId),value就是客户端信息(可以根据需求灵活设置)。
sessionId
是由服务器生成的一个 “唯一性字符串”,用来唯一标识用户的身份信息。- 从 Session 机制的角度来看,这个唯一性字符串称为
sessionId
,但是站在整个登录流程中看待,也可以把这个唯一性字符串称为token
。- 即
sessionId
和token
可以理解成是同一个东西在不同视角的不同叫法。- 例如:
服务端与客户端维持会话的基本流程:
- 当用户登录的时候,服务器会在 Session 中新增一条新的记录,并把生成的
sessionId
通过Set-Cookie
字段返回给客户端,然后将其保存在浏览器中。 - 客户端后续再向服务端发起请求的时候,都会在请求中将 Cookie 中的
sessionId
通过Cookie
字段发送给服务器。 - 服务器在收到用户请求后,会根据请求中的
sessionId
在 Session 中找到与这个用户对应的用户信息,维持与该用户的会话,并处理请求任务。
另外,Servlet 中的 Session 默认是保存到内存中的,那么就意味着:如果重启服务器则 Session 数据就会消失。为了避免这个问题,可以使用其他方式存储 Session 数据,如数据库或分布式缓存。
7.3 实现用户登录案例
在用户登录案例中,通常的流程如下:
- 用户在登录页面输入用户名和密码,点击登录按钮提交表单。
- 服务器接收表单提交的数据,验证用户身份。
- 如果验证成功,服务器创建一个 Session,将用户信息存储在其中,并生成一个会话标识(Session ID)。
- 服务器通过
Set-Cookie
响应头将会话标识发送给客户端,存储在客户端的 Cookie 中。- 客户端在后续的每个请求中自动包含会话标识的 Cookie,以便服务器识别用户。
- 服务器通过会话标识从会话存储中取出用户信息,识别用户身份,实现用户的登录状态。
以下是简单的登录案例:
这个代码中主要是通过 HttpSession
类完成,并不需要我们手动操作 Cookie 对象。
- 创建
LoginServlet
类,用于实现登录逻辑。
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf8");
resp.setContentType("text/html; charset=utf8");
// 1. 获取输入的用户名和密码
String username = req.getParameter("username");
String password = req.getParameter("password");
// 2. 判断用户名和密码是否合法
if(!username.equals("admin") || !password.equals("123456")){
resp.getWriter().write("用户名或密码错误!");
return;
}
// 3. 登录成功,设置 Session
HttpSession session = req.getSession(true); // Session 不存在则创建
session.setAttribute("username", username);
session.setAttribute("loginCount", 0);
// 4. 重定向到 index
resp.sendRedirect("index");
}
}
- 创建
IndexServlet
类,用于判断用户是否登录,如果登录则显示用户信息。
@WebServlet("/index")
public class IndexServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf8");
resp.setContentType("text/html; charset=utf8");
// 1. 获取 Session,判断用户是否登录
HttpSession session = req.getSession(false); // Session 不存在也不创建
if(null == session){
// 用户没有登录,重定向到 login.html
resp.sendRedirect("login.html");
return;
}
// 2. 已经登录,获取 Session 中保存的用户名和登录次数
String username = (String)session.getAttribute("username");
Integer loginCount = (Integer) session.getAttribute("loginCount");
loginCount += 1;
session.setAttribute("loginCount", loginCount);
// 3. 展示用户信息到页面上
StringBuilder respBody = new StringBuilder();
respBody.append(String.format("<div>用户名:%s</div>", username));
respBody.append(String.format("<div>loginCount: %d</div>", loginCount));
resp.getWriter().write(respBody.toString());
}
}
- 在
webapp
目录下创建login.html
,用于提交登录用户名和密码。
<form action="login" method="post">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit" value="登录">
</form>
- 运行程序
访问 http://localhost:8080/hello_servlet/index
,由于没有登录,跳转至 login.html
:
正确输入用户名和密码:
成功登录,跳转至 index
,成功显示 Session 中设置的用户信息:
八、上传文件操作
文件上传是 Web 开发中常见的操作,Java Servlet 提供了处理文件上传的支持。在 Java Servlet 中,文件上传通常使用 javax.servlet.http.Part
类或 javax.servlet.http.HttpServletRequest
的相关方法来实现。
8.1 核心方法
HttpServletRequest
接口相关方法:
方法 | 说明 |
---|---|
Part getPart(String name) |
获取请求中给定的 name 文件 |
Collection<Part> getParts() |
获取所有文件 |
Part
类相关方法:
方法 | 说明 |
---|---|
String getSubmittedFileName() |
获取提交的文件名 |
String getContentType() |
获取提交的文件类型 |
long getSize() |
获取文件的大小 |
void write(String path) |
把提交的文件数据写入磁盘文件 |
8.2 提交图片到服务器
1. 在 webapp
目录下,创建 upload.html
用于提交图片文件。
<body>
<form action="upload" enctype="multipart/form-data" method="post">
<input type="file" name="image">
<input type="submit" value="提交图片">
</form>
</body>
上传文件一般通过 POST 请求的 form
表单实现,并且在 form
标签中要加上 multipart/form-data
字段。
2. 创建 UploadServlet
类,用于处理客户端提交图片的请求。
@WebServlet("/upload")
@MultipartConfig
public class UploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 获取图片文件
Part image = req.getPart("image");
// 2. 打印图片信息
System.out.println("图片名称:" + image.getSubmittedFileName());
System.out.println("图片类型:" + image.getContentType());
System.out.println("图片大小:" + image.getSize());
// 3. 将图片保存到磁盘
image.write("D:/image/tomcat.jpg");
resp.getWriter().write("upload OK!");
}
}
注意事项:
- 需要给
UploadServlet
加上@MultipartConfig
注解,否则服务器代码无法使用getPart
方法。getPart
的参数需要和form
表单中input
标签的name
属性对应。- 客户端一次可以提交多个文件(使用多个
input
标签),此时服务器可以通过getParts
获取所有的Part
对象。
3. 运行程序,并提交一张图片。
此时发现 IDEA 控制台打印的信息:
D盘的 image
目录下多了一张图片: