Spring MVCを導入 (+ MySQL, Redis)

Spring MVCが動作する仕組みについて、設定等が理解できるように、最初にイメージできるように整理しておきます。大きく以下の流れとなります。

  1. Front ControllerとなるDispatcherServletでリクエストを受付

  2. XMLファイルからハンドラーマッピングのエントリーを取得し、Controllerへリクエストを転送

  3. ControllerはModelAndViewのオブジェクトを返す

  4. DispatcherServletはXMLファイル中のViewResolverのエントリーを確認し、指定されたビューのコンポーネントを呼び出す

(引用) 22. Web MVC framework

(参考) Spring MVC Tutorial

Spring MVCでHello,Worldを表示 (Mavenでパッケージ管理)

実際にプロジェクトを作成していきます。以下の手順でMaven管理による Spring MVCのプロジェクトを作成します。

  1. File > New > Dynamic Web Project を選択

  2. 遷移先の画面で Project Name: HayashierWebsite のようにプロジェクト名を入力

  3. Target runtimeで[New Runtime...]を選択

  4. Apache Tomcat v9.0を選択し、[Create a new local server]を選択

  5. Tomcatのサイトから9.0.34のzipをダウンロードして展開

  6. 以下のように入力。Tomcatのディレクトリは上記ファイルをダウンロードしてきて展開したものの場所。JREは使用しているJavaのバージョンを確認して選択。 

    • Name: Apache Tomcat v9.0

    • Tomcat installation directory: /Users/hayashier/Downloads/apache-tomcat-9.0.34

    • JRE: Java SE 11.0.5 [11.0.5]

  7. プロジェクト名を右クリックして、Configure > Convert to Maven Project と選択

  8. 遷移先の画面で以下を入力して、Finishを選択

    • Group Id: hayashier.website

    • Artifact Id: hayashier-website

  9. 生成されたpom.xmlを編集して以下のようなXMLファイルにします。

  10. プロジェクト名を右クリックして、Java EE Tools > Generate Deployment Descriptor Stub と選択してweb.xmlを生成

  11. 生成されたweb.xmlのweb-appタグ内に以下を追加

  12. web.xml中のservlet-nameタグに記載されている名前がサーブレット名でWEB-INF/以下に[servlet-name]-servlet.xmlという名前の設定ファイルをispatcherServlet初期化後に参照されるようになります。ここではmywebsite-servlet.xmlというファイル名で以下の内容を記載します。XMLやアノテーションの使用より Spring 3 で導入された JavaConfig を利用することも多いかと思いますが、ここではXMLによる例となります。

  1. その他、CSS/JavaScriptを利用できるように[servlet-name]-servlet.xmlのbeansタグ内に以下を追加しておきます。ここでは、ローカルの開発環境での開発効率向上を優先してCSSはキャッシュを無効化していますが、不要な場合、mvc:resource-chainタグおよびその中身は不要です。

  1. 同ファイル内に共通ヘッダーを作成できるように以下も追加しておきます。

  1. 現状の設定のままだと、http://localhost:8080/hayashier-website/ でないとアクセスできない状態になっている。ルートアクセスするために、ServersタブでTomcat v 9.0 Server at localhostとなっている部分をクリックして展開

  2. Modulesタブを選択すると、Pathが/hayashier-websiteとなっていることが確認

  3. Pathを選択した状態で、[Edit...]を選択し、/に変更して再起動することでルートアクセスできるようになる。

  4. 再度15を実行

  5. Portsの項目でHTTP/1.1が8080になっているので、80に変更。その後、ポート番号を指定しなくてもアクセスできるようになる。

  6. Run As > Run on Server を選択

  7. http://localhost でアクセスできるようになる

Summary

ここまでで作成したファイルは以下のようになります。

  • pom.xml

  • WebContent/web.xml

  • WebContent/mywebsite-servlet.xml

  • WebContent/index.jsp

Spring MVCによるルーティング

リクエストパスと実際に行う処理について、対象のControllerには@Controllerのアノテーションをつけ、処理を行いたいメソッドに対してパスを定義した@RequestMappingのアノテーションを付与します。 例えば、以下のようにTopControllerを作成して@Controllerのアノテーションを付与し、helloWorld()メソッドに@RequestMapping("/top")のアノテーションを付与することで、http://localhost/top のようにアクセスすることでhelloworld()メソッドによる処理が行われるようになります。

return new ModelAndView("top", "message", message)のように第1引数で指定した文字列の JSP ファイルのビューを返し、第3引数に渡した値に対し第2引数に渡した文字列をビューで指定することで呼び出すことができます。ここでは、"message"という文字列を第2引数として、第3引数に<h1>Hello, World!</h1>を渡しているため、ビューから${message}のように呼び出すことで、ビュー上で<h1>Hello, World!</h1>を表示させることができます。

  • src/contoller/TopController.java

  • WebContent/WEB-INF/jsp/top.jsp

Spring MVCにMySQL導入

pom.xml中のprojectタグに以下を追加

projectタグ内のdependenciesタグに以下を追加

mywebsite-servlet.xmlのbeansタグにデータベースへアクセスするためのドライバー情報を追加

最小構成では、Controllerで以下のようにアクセス可能です。

実際にはControllerに全てを記述せずにモデル等を定義することが多いかと思います。

ここでは、Articleのオブジェクトを表すクラスを以下のように定義します。

Mapperを定義します。

DAOを定義します。

Controllerから以下のように使用することができる

MySQL上で以下のようにDDLを定義して、サンプルデータを挿入しておきます。

設定ファイルを外部ファイル化

MySQLのホスト情報やユーザー名やパスワードを外部ファイル化しておきます。ファイルの場所は相対ディレクトリ指名やclasspath:,file:/// or file: が使用できます。

mywebsite-servlet.xmlにファイルの場所を示す以下のbeanタグを追加します。

また、外部ファイルから読み込んだ値を設定するために同ファイル内に以下のbeanタグを追加しておきます。 

WebContent/WEB-INF以下にclassesディレクトリを作成し、WEB-INF/classes ディレクトリに.propertiesのファイルを配置します。

Spring MVCにRedisの導入

pom.xmlに以下を追加

mywebsite-servlet.xmlの<value>classpath:db.properties</value>の下に<value>classpath:cache.properties</value>を追加してRedisの設定ファイルの場所を指定します。

外部ファイルで指定した値を読み込んで設定等行うために以下を追加します。

WebContent/WEB-INF/classes以下にcache.propertiesという名前で以下の内容のファイルを作成します。

DAOファイルを定義。ここれではRedisTemplateのHash型を使用しています。

モデルをSerializableのインターフェイスとして実装するように変更しておきます。

あとは使う場所のクラスでメンバー変数として宣言して使用します。

要件に応じて、SQLで取得する前にキャッシュから取得して、データがなければ、SQLで取得し、その後キャッシュも更新するといった実装をします。

Tomcat上にSpring MVCのプロジェクトをデプロイ

先程はEclipseで組み込みの機能のTomcatを利用して動作確認をしていましたが、MacOS上にTomcatをインストールしてこの上で、Spring MVCを動作させます。上記でSpring MVCをwarファイルとして作成していたため、これをTomcatでWebサーバとして稼働させるものの上に配置して動作させます。

  1. 以下のようにHomebrewでTomcatをインストールして起動します。

  2. http://localhost でTomcatのデフォルトページにアクセスできるようになります。 

  3. Tomcatで稼働するWebサーバのルートディレクトリの場所の確認をおこないます。brew installからインストールした場合、/usr/local/Cellar/tomcat/9.0.34/libexec/webapps/のディレクトリ(9.0.34の部分は、tomcatで使用しているバージョン)となり、こちらに移動しておきます。上記ディレクトリはbrew ls tomcatコマンドで確認でき、webappsディレクトリ以下が実際にWebサーバーとして稼働させるデフォルトのルートディレクトリとなります。

  4. warファイルをwebapps以下に配置

    • cp ~/eclipse-workspace/HayashierWebsite/target/hayashier-website-0.0.1-SNAPSHOT.war .

  5. 以下のURLでアクセスできるようになる

    • http://localhost:8080/hayashier-website-0.0.1-SNAPSHOT/

  6. server.xmlについて、コメントアウト等は除き、主要箇所は全体として以下のようになります。ここでは、Connectorタグでポート番号を8080番から80番に変更。hayashier-website-0.0.1-SNAPSHOT.warはwebapps以下でディレクトリとして展開されますが、このディレクトリをappBase="webapps"の属性を持つHostタグ内のContextタグに対して、docBase="hayashier-website-0.0.1-SNAPSHOT"のように指定します。 

  7. Tomcatを再起動

  8. http://localhost で作成していたアプリケーションが見られるようになります。

Error

cannot resolve to be a type のようにでる

importしてるやつも一度削除して、Eclipseによるimportの提案に従って追加

突然Springが機能しなくなる

Consoleには以下のエラー文

  • Tomcatを停止

  • Projectメニューから[Clean...]

  • ServersタブからTomcatを右クリックして[Clean...]

  • Tomcatをクリックし、Modulesタブでパスが初期状態になっているので変更(例. /hayashier-website から / に変更)

  • Tomcatを起動し、プロジェクトをサーバー上で稼働

(参照) No Spring WebApplicationInitializer types detected on classpath

Request processing failed; nested exception is org.springframework.data.redis.serializer.SerializationException: Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [hayashier.mywebsite.model.Article]

対象のモデルにimplements Serializableを追加して、Serializableのインターフェイスとして実装

(参照) DefaultSerializer requires a Serializable payload but received an object of type [model.Admin]

ルートパスでアクセスできない

Tomcatのデフォルトページへのアクセスが http://localhost:8080/hayashier-website/ のURLとなるので、/hayashier-websiteを省略した形でアクセスできるようにするための設定手順となります。

  1. ルートアクセスするために、ServersタブでTomcat v 9.0 Server at localhostとなっている部分をクリックして展開

  2. Modulesタブを選択すると、Pathが/hayashier-websiteとなっていることが確認

  3. Pathを選択した状態で、[Edit...]を選択し、/に変更することでルートアクセスできるようになる。

(参考) SpringSource IDE does not use project name as root URL for Spring MVC application

Port番号を8080から80番へ変更

以下、Eclipse組み込みのTomcatの設定変更手順となります。

  • ServersタブでTomcat v 9.0 Server at localhostとなっている部分をクリックして展開

  • Portsの項目で、HTTP/1.1を8080から80に変更

(参考) How to change port numbers for Tomcat in Eclipse

  • Eclipse組み込みでばうTomcatのデフォルト設定を変更するためには、conf/server.xmlの以下のようなConnectorタグを8080番ポートを80番ポートへ変更

(参考) Change Tomcat port numbers in server.xml file

パスにプロジェクト名を含むスナップショットのディレクトリ名ではなくルートディレクトリでアクセスできるように変更

server.xmlのHostタグ内の以下のように変更

jspファイルのままでレンダリングされない

以下のタグは<!DOCTYPE html>の後に配置

the type is not generic; it cannot be parameterized with arguments <Map<String,String>>

以下のようにインターフェイスではなく具象クラスで宣言

重大: パス[]を持つコンテキスト内のサーブレット[mywebsite]のServlet.service() が例外[Handler processing failed; nested exception is java.lang.NoClassDefFoundError: javax/servlet/jsp/jstl/core/Config]が根本的要因と共に投げられました。 java.lang.ClassNotFoundException: javax.servlet.jsp.jstl.core.Config

以下のエラーが発生

pom.xmlに以下を追加してMavenプロジェクトをビルドし直す

~.htmlの識別子をつけるとjspがrenderingされるが、識別子をつけない場合、renderingされない

pom.xmlに以下を追加

web.xmlのurl-patternを変更

Last updated