JavaRush /Java Blog /Random-JA /Hello World から Spring Web MVC まで、そしてサーブレットはそれとどのような関係があるの...
Viacheslav
レベル 3

Hello World から Spring Web MVC まで、そしてサーブレットはそれとどのような関係があるのか

Random-JA グループに公開済み
Hello World から Spring Web MVC まで、そしてサーブレットはそれと何の関係があるのか​​ - 1

導入

ご存知のとおり、Java の成功はまさにネットワークへの接続を目指すソフトウェアの進化のおかげです。そこで、通常のコンソールアプリケーション「Hello World」をベースにして、コンソールアプリケーションからネットワークアプリケーションになるためには何が必要かを理解していきます。したがって、最初に Java プロジェクトを作成する必要があります。プログラマーは怠け者です。先史時代、マンモスを狩っている人もいれば、座ってさまざまな Java ライブラリやディレクトリ構造に混乱しないように努めている人もいました。開発者がアプリケーションの作成プロセスを制御して、「バージョン 2 のこれこれのライブラリが欲しい」と簡単に書けるように、開発者は特別なツール、つまりシステムを構築することを考え出しました。最も有名なものはMavenGradleの 2 つです。この記事では Gradle を使用します。以前はディレクトリ構造を自分で作成する必要がありましたが、現在ではGradle Init Plugin を使用して、 1 つのコマンドでディレクトリ構造と基本 Main クラスを持つ Java プロジェクトを作成できます。 gradle init --type java-application このコマンドは、次のコマンドで初期化 (init) を実行します。コンソール Hello World を備えた Java アプリケーション (java-application ) を使用します。完了すると、ファイルbuild.gradleがディレクトリに表示されます。これはビルド スクリプトです。つまり、アプリケーションを作成するための特定のスクリプトと、そのためにどのようなアクションを実行する必要があるかを記述したものです。それを開いて次の行を追加してみましょう。jar.baseName = 'webproject' Gradle を使用すると、プロジェクトに対してさまざまなアクションを実行できます。これらのアクションはタスクと呼ばれます。コマンド (タスク) を実行すると、/build/libsgradle buildディレクトリにJAR ファイルが作成されます。そして、ご想像のとおり、その名前はwebproject.jarになります。しかし、 を実行すると、エラーが発生します。これは、Java アプリケーションの場合、マニフェストを添付する必要があるためです。これは、アプリケーションの操作方法、アプリケーションの認識方法についての説明です。次に、Java アプリケーションを実行する JVM は、どのクラスがプログラムへのエントリ ポイントであるか、およびその他の情報 (クラスパスなど) を認識します。ビルド スクリプトの内容を詳しく見てみると、プラグインが接続されていることがわかります。例: Gradle Java Plugin ページ に移動すると、マニフェストを構成できることがわかります。 java -jar ./build/libs/webproject.jarno main manifest attributeapply plugin: 'java'
jar {
    manifest {
        attributes 'Main-Class': 'App'
    }
}
プログラムへのエントリ ポイントであるメイン クラスは、Gradle Init Plugin によって生成されました。そして、それは mainClassName パラメータでも指定されます。しかし、これは私たちには合わなかったからです... この設定は、別のプラグインであるGradle Application Pluginを参照します。これで、画面に Hello World を表示する Java アプリケーションができました。この Java アプリケーションは JAR (Java ARchive) にパッケージ化されています。シンプルでコンソールベースですが、最新のものではありません。それをWebアプリケーションに変えるにはどうすればよいでしょうか?
Hello World から Spring Web MVC まで、そしてサーブレットはそれと何の関係があるのか​​ - 2

サーブレットAPI

Java がネットワークと連携できるようにするために、サーブレット APIと呼ばれる仕様が古くに登場しました。この仕様では、クライアント (ブラウザーなど) からメッセージを受信し、応答 (ページのテキストなど) を送信する、クライアントとサーバーの対話について説明します。当然のことながら、それ以来多くのことが変わりましたが、重要なのは、Java アプリケーションを Web アプリケーションにするために、サーブレット API が使用されるということです。根拠のない憶測を避けるために、まさにその仕様、 JSR-000340 JavaTM Servlet 3.1を取り上げましょう。まず気になるのは「第1章:概要」です。私たちが理解する必要がある基本的な概念について説明します。まず、サーブレットとは何でしょうか? 「 1.1 サーブレットとは?」の章では、サーブレットはコンテナによって管理され、動的コンテンツを生成する Java コンポーネントであると述べています。他の Java コンポーネントと同様、サーブレットはバイトコードにコンパイルされる Java クラスであり、Java テクノロジを使用して Web サーバーにロードできます。サーブレットは、サーブレット コンテナによって実装される要求/応答パラダイムのフレームワーク内で Web クライアント (ブラウザなど) と対話することが重要です。サーブレットはある種のサーブレット コンテナ内に存在することがわかりました。これは何ですか?「 1.2 サーブレット コンテナとは? 」の章では、サーブレット コンテナは、リクエストの送信や応答の送信に使用されるネットワーク サービスを提供する Web サーバーまたはアプリケーション サーバーの一部であると説明されています。このサーブレット コンテナ自体がサーブレットのライフ サイクルを管理します。すべてのサーブレット コンテナは少なくとも HTTP プロトコルをサポートする必要がありますが、他のプロトコルをサポートする場合もあります。たとえば、HTTPS。サーブレット コンテナが、サーブレットが実行される環境にセキュリティ関連の制限を課すことができることも重要です。「 10.6 Web アプリケーション アーカイブ ファイル」に従って、Web アプリケーションは WAR (Web ARchive) ファイルにパッケージ化する必要があることも重要です。つまり、別のもののために jar とアプリケーションのプラグインを削除する必要があります。これがGradle WAR プラグインです。そして、jar.baseNameの代わりにwar.baseNameを指定します。jar プラグインを使用しなくなったため、マニフェスト設定も削除しました。JAR を起動するとき、マニフェストを通じて Java 仮想マシン (JVM) にアプリケーションの操作方法を指示する必要がありました。JVM が実行していたからです。Web アプリケーションは、明らかに、ある種の Web サーバーによって実行されます。彼は、何らかの方法で Web アプリケーションの操作方法を伝える必要があることがわかりました。そして、そのとおりであることがわかりました。Web アプリケーションには独自の特別なマニフェストがあります。これはデプロイメント記述子と呼ばれます。セクション全体が「14. デプロイメント記述子」に当てられています。重要なセクションがあります:「第 10 章:サーブレット API の観点から Web アプリケーションとは何かについて説明します。たとえば、「10.5 ディレクトリ構造」の章では、デプロイメント記述子をどこに置くべきかが示されています。/WEB-INF/web.xmlWEB-INF をどこに配置するか? Gradle WAR プラグインで述べたように、新しいレイアウトを追加します: src/main/webapp. したがって、そのようなディレクトリを作成しましょう, 中に WEB-INF ディレクトリを作成し、その中に web.xml ファイルを作成します. ディレクトリが重要です.これは META-INF ではなく WEB-INF と呼ばれます! 「14.5.1 基本的な例」の XML 例からコピーしてみましょう:
Hello World から Spring Web MVC まで、そしてサーブレットはそれと何の関係があるのか​​ - 3
ご覧のとおり、構成には XML ドキュメントが使用されます。XML ドキュメントが有効 (Valid) であるとみなされるには、何らかの「スキーマ」に準拠している必要があります。これは、XML ドキュメントの一種のインターフェイスと考えることができます。スキーマは、XML ドキュメント内にどの要素を含めることができるか、要素を定義できるデータのタイプ、順序、要件、およびその他の側面を指定します。ドキュメントからコピーした例ではバージョン 2.5 を示していますが、ここではバージョン 3.1 を使用したいと考えています。当然、バージョンが変わると仕様が変わり、新しい機能が追加されます。したがって、バージョン 2.5 (web-app_2_5.xsd) で使用されたスキーマ以外のスキーマを使用する必要があります。バージョン 3.1 ではどのスキームを使用すればよいですか? これについてはドキュメントの「14.3 デプロイメント記述子」の章で説明されていますspecification is available at http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd 。つまり、スキーマへのリンクをすべての場所で指定された xsd に置き換える必要があり、それを 3.1 に変更することを忘れずにversion="2.5"、すべての場所で名前空間も変更する必要があります ( xmlns および xsi:schemaLocation)。これらは、どの名前空間内で作業するかを示します (非常に簡単に言うと、どの要素名を使用できるか)。スキーマ ファイルを開くと、targetNamespace には、指定する必要があるのと同じ名前空間が含まれます。
Hello World から Spring Web MVC まで、そしてサーブレットはそれと何の関係があるのか​​ - 4
覚えているように、Jar ファイルのマニフェストには、使用するクラスを記述しました。ここで何をすればいいでしょうか?ここでは、Web クライアントからリクエストを受信したときに使用するサーブレット クラスを指定する必要があります。説明は「14.4 デプロイメント記述子の図」の章を参照してください。次のようになります。
Hello World から Spring Web MVC まで、そしてサーブレットはそれと何の関係があるのか​​ - 5
ここではすべてがシンプルです。サーバーレットは宣言され、特定のテンプレートにマップされます。この場合、/app 上にあります。テンプレートが実行されると、サーブレット メソッドが実行されます。美しくするために、xml 構成を修正することを忘れずに、App クラスをパッケージに転送する必要があります。しかし、それだけではありません。アプリはサーブレットである必要があります。サーブレットであるとはどういう意味ですか? これは、 HttpServletから継承する必要があることを意味します。例は「 8.1.1 @WebServlet 」の章にあります。それによると、App クラスは次のようになります。
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class App extends HttpServlet {
    public String getGreeting() {
        return "Hello world.";
    }

	public void doGet(HttpServletRequest request, HttpServletResponse response) {
		response.setContentType("text/html");
		try {
			response.getWriter().println(getGreeting());
		} catch (IOException e) {
			throw new IllegalStateException(e);
		}
	}
}
しかし、私たちのプロジェクトはまだ準備ができていません。現在はサーブレット API バージョン 3.1 に依存しているためです。これは、ビルド スクリプトでサーブレット API への依存関係を示す必要があることを意味します。JVM は、コードに記述された内容が正しいことと、そのコードの使用方法を認識する必要があります。覚えているとおり、仕様は本質的に、すべてがどのように機能するかを説明する単なるインターフェイスです。そして実装は Web サーバー側にあります。したがって、サーブレット API がなければ、Maven Central で必要なライブラリを見つけます: javax.servlet-apiそして、依存関係ブロックにエントリを追加します。Maven リポジトリでは、ご覧のとおり、provided と表示されます。依存関係を使用する前に、スコープを指定する必要があります。Gradle には「provided」という名前のスコープはありませんが、「コンパイルのみ」スコープがあります。したがって、次のことを示します。「providedCompile 'javax.servlet:javax.servlet-api:3.1.0' うーん、すべてが順調のようですね?」Gradle Build はプロジェクトを WAR ファイルにビルドします。そして、次に何をすべきでしょうか? まず、Web サーバーが必要です。Googleでは「 WebサーバーJavaリスト」と書くとWebサーバーのリストが表示されます。このリストから、たとえばTomCatを選択してみましょう。Apache Tomcat Web サイトに移動し、最新バージョン (現在バージョン 9) を zip アーカイブとしてダウンロードします (Windows の場合)。それを何らかのディレクトリに解凍します。やあ、Web サーバーがあります。Web サーバー ディレクトリのbinサブディレクトリから、コマンド ラインからcatalinaを実行し、利用可能なオプションを確認します。やろう:catalina start。各 Web サーバーには、Web サーバーが監視するディレクトリがあります。そこに Web アプリケーション ファイルが表示されると、Web サーバーはそのファイルのインストールを開始します。このインストールは、デプロイメントまたはデプロイメントと呼ばれます。はいはい、だから「デプロイメント記述子」です。つまり、アプリケーションを適切にデプロイする方法です。Tomcat では、このディレクトリはwebappsです。そこにgradle buildを使って作ったwarをコピーしてみましょう。この後、ログには次のような内容が表示されます。 Deployment of web application archive [tomcat\webapps\webproject.war] has finished in [время] ms さらにわかりやすくするために、tomcat ディレクトリでファイルを編集し\conf\tomcat-users.xml、次の行を追加します。
Hello World から Spring Web MVC まで、そしてサーブレットはそれと何の関係があるのか​​ - 6
ここでサーバーを再起動し (catalina stop、catalina start)、アドレスに移動すると、http://127.0.0.1:8080/manager すべてのアプリケーションのパスが表示されます。私たちの Web プロジェクトには、おそらくパス /webproject が与えられています。この道は何でしょうか?「 10.1 Web サーバー内の Web アプリケーション」の章の仕様では、Web アプリケーションがアプリケーション内のパス (この場合は /webproject) に関連付けられると述べています。このパスを経由するすべてのリクエストは、同じ ServletContext に関連付けられます。このパスはcontextRootとも呼ばれます。そして、「 10.2 ServletContextとの関係」によれば、サーブレットコンテナはWebアプリケーションとServletContextを1対1で関連付けます。つまり、各 Web アプリケーションには独自の ServletContext があります。ServletContextとは何ですか? 仕様に記載されているように、ServletContext は、サーブレットが実行されている「アプリケーションのビュー」をサーブレットに提供するオブジェクトです。サーブレット コンテキストについては、サーブレット API 仕様の第 4 章で詳しく説明されています。驚くべきことに、バージョン 3.1 のサーブレット API では web.xml が存在する必要がなくなりました。たとえば、アノテーションを使用してサーブレットを定義できます。
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("/app2")
public class App2 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html");
        response.getWriter().println("app2");
    }
}
「 Java EE インタビュー - JEE サーブレット API (質問と回答) 」 というトピックについてもお勧めします。したがって、サーブレットがあり、Web クライアントにどのような応答を返すかを担当します。ユーザーからのリクエストを受け取り、アクセスされたパスとサーブレットへのパスを照合し、一致するものが見つかった場合はサーブレットを実行する ServletContainer があります。大丈夫。この世界の絵の中で春はどの位置を占めていますか?

Spring Web MVC

すばらしい、Web アプリケーションができました。次に Spring を接続する必要があります。どうすればこれができるでしょうか?まず、Spring をプロジェクトに適切に接続する方法を理解する必要があります。以前はSpring プラットフォームプロジェクトのドキュメントに従ってこれを実行できたことが判明しましたが、現在は「プラットフォームは 2019 年 4 月 9 日にサポート期間が終了します」、つまり、これはお勧めできません。それを使ってください、なぜなら すぐにサポートされなくなる予定です。唯一の解決策は、「プラットフォームのユーザーは Spring Boot の依存関係管理の使用を開始することをお勧めします」です。したがって、 Spring Boot のドキュメントに進みましょう。Spring Boot 自体は使用せず、Spring Boot の依存関係管理のみを使用することを明確にしておきます。つまり、Spring Boot プロジェクトは、(Spring MVC を含む) ライブラリのどのバージョンを使用するかに関する情報を提供できます。そこで 3.2 を見つけます。Spring Boot の依存関係管理を単独で使用する。ドキュメントによると、ビルド スクリプトに次のコードを追加します。
plugins {
    id 'org.springframework.boot' version '2.0.4.RELEASE' apply false
}
apply plugin: 'io.spring.dependency-management'
そして
dependencyManagement {
    imports {
        mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
    }
}
ご覧のとおり、私たちは を示しましたapply false。Spring Boot 自体は使用しませんが、そこから依存関係管理を使用します。この依存関係管理は、BOM (「部品表」) とも呼ばれます。これで、Spring Web MVC プロジェクト自体を接続する準備が整いました。Spring Web MVC はSpring Frameworkプロジェクトの一部であり、「 Web Servlet 」セクションに興味があります。依存関係をビルド スクリプトに追加しましょうcompile 'org.springframework:spring-webmvc'。ご覧のとおり、スコープのコンパイルを設定します。Web サーバーは Spring を提供しません。私たちのプロジェクトは、その内部に Spring ライブラリを強制的に含める必要があります。次に、セクション「 1.2. DispatcherServlet 」を読むことが重要です。このセクションでは、Spring MVCは「フロント コントローラー」パターンを中心に構築されており、構成と他のコンポーネントへの委任を提供するある種の中央サーブレットがあると述べられています。 。ディスパッチャーはディスパッチャーと訳せます。したがって、まず web.xml で次のように宣言します。
Hello World から Spring Web MVC まで、そしてサーブレットはそれと何の関係があるのか​​ - 7
ご覧のとおり、これは実際にはサーブレット API 仕様で定義された通常のリスナーです。より正確に言うと、これは ServletContextListener です。つまり、Web アプリケーションのサーブレット コンテキストを初期化するためにトリガーされます。次に、設定を含む特別な XML 構成がどこにあるかを Spring に伝える設定を指定する必要があります。
Hello World から Spring Web MVC まで、そしてサーブレットはそれと何の関係があるのか​​ - 8
ご覧のとおり、これはサーブレット コンテキスト レベルで保存される通常の設定ですが、アプリケーション コンテキストを初期化するときに Spring によって使用されます。ここで、すべてのサーブレットの代わりに、他のすべてのリクエストを分散する 1 つのディスパッチャを宣言する必要があります。
Hello World から Spring Web MVC まで、そしてサーブレットはそれと何の関係があるのか​​ - 9
そしてここには魔法はありません。見てみると、これは HttpServlet であり、Spring がそれをフレームワークにする多くのことを行う場所です。残っているのは、特定の URL テンプレートをサーブレットと相関付ける (マッピングする) ことだけです。
Hello World から Spring Web MVC まで、そしてサーブレットはそれと何の関係があるのか​​ - 10
すべては以前と同じです。次に、Web サーバーが表示するものを作成しましょう。たとえば、WEB-INF に Pages サブディレクトリを作成すると、hello.jsp ファイルが作成されます。内容は最も原始的なものになる可能性があります。たとえば、HTML タグ内には、テキスト「Hello World」を含む h1 タグがあります。applicationContext.xml前に指定したファイルを作成することを忘れないでください。Spring ドキュメント「 1.10.3. クラスの自動検出と Bean 定義の登録」から例を見てみましょう。
Hello World から Spring Web MVC まで、そしてサーブレットはそれと何の関係があるのか​​ - 11
なぜなら この方法で自動検出を有効にすると、2 つのクラスを作成できるようになります (特別な Spring アノテーションを使用するため、これらは Spring Bean とみなされます)。Spring はこれらのクラスを自身で作成し、その助けを借りてアプリケーションをカスタマイズします。
  1. Web 構成 (Java スタイル構成の例):

    @Configuration
    @EnableWebMvc
    public class WebConfig implements WebMvcConfigurer {
        @Override
        public void configureViewResolvers(ViewResolverRegistry registry) {
            registry.jsp("/WEB-INF/pages/", ".jsp");
        }
        @Override
        public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
            configurer.enable();
        }
    }

    この例は、Spring Framework のドキュメント「1.11. MVC Config」で説明されています。

    ここでは、JSP ページがどこにあるかを判断するのに役立つ ViewResolver を登録します。2 番目の方法では、「Default servlet」が確実に有効になります。

    詳細については、「default-servlet-handler の必要性と使用方法」を参照してください。

  2. 特定の JSP へのリクエストのマッピングを記述するための HelloController コントローラー

    @Controller
    public class HelloController {
        @GetMapping("/hello")
        public String handle(Model model) {
            return "hello";
        }
    }

    ここでは、ドキュメントの「1.4. アノテーション付きコントローラー」の章で説明されている @Controller アノテーションを使用しました。

これで、アプリケーションがデプロイされ、リクエスト/webproject/hello(/webproject がコンテキスト ルート) を送信すると、DispatcherServlet が最初に処理されます。メインのディスパッチャとして、 /* 現在のリクエストと一致すると判断します。これは、DispatcherServlet が何らかの処理を行う必要があることを意味します。次に、見つかったすべてのマッピングを調べます。/hello にマップされたハンドル メソッドを持つ HelloController があることが確認され、それが実行されます。このメソッドは「hello」というテキストを返します。このテキストは ViewResolver によって受信され、クライアントに表示する必要がある JSP ファイルをどこで探すかをサーバーに指示します。したがって、クライアントは最終的にその非常に大切なページを受け取ることになります。

結論

「コンテキスト」という言葉が怖いものではないことがこの記事から明らかだと思います。この仕様は非常に便利です。そしてドキュメントは私たちの敵ではなく友人です。Spring が何に基づいているのか、Spring がどのように接続しているのか、Servlet API が Spring とどのように関係しているのかが明確になることを願っています。
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION