Spring MVC についてまとめてみるよ!!!


注意

これは自分用のメモです。未熟な雑魚Springユーザーが書いたものなので、きっと間違いもあります。ご容赦ください

Spring MVC

MVCパターンはwebアプリケーションの開発に使われるパターンで、初めてreilsでポートフォリオを作ろうとしたときに僕は初めて知りました。ただ、SpringMVCは厳密にはフロントコントローラーパターンと呼ばれるアーキテクチャパターンで、僕も十分に理解できていないが、全てのリクエストを一旦受け取るフロントコントローラーとと呼ばれるコントローラーを実装したパターンらしい。
一方、SpringMVCの特徴は以下の通りです。

1. POJOで実装されてる
POJOってのはシンプルなJAVAのオブジェクトって意味。フレームワーク独自のインターフェースを実装する必要がない!
2. アノテーションを使って開発
リクエストマッピング(ここにアクセスしたらこのメソッドを実行するとか)などをアノテーションを使って記述するのでスッキリ簡単にかける。
3. メソッドシグネチャの定義を柔軟にできる
Controllerクラスのメソッドの引数には様々な型を指定できる。引数に関しても多くの型に対応している。
4. ServletAPIの抽象化
ServletAPIが抽象化できると、Controllerクラスの実装からServletAPIを使うコードを排除できるとのこと。これがどのくらいいいことなのかわかってはいないが、とりあえずコードの記述量が減るっぽい。
5. viewの実装技術の抽象化
viewの実装技術を意識しなくても、view名さえ指定してあげればviewを返却できる。
6. SpringのDIコンテナとの連携
SpringのDIやAOPを活用することができる。

SpringMVCのアーキテクチャパターンを知ろう

SpringMVCではフロントコントローラーパターンと言うアーキテクチャパターンを持っていて、全てのリクエストを一旦フロントコントローラーが受け取り、その後適切なControllerを探しまっせ。と言う感じらしい。
さらにフロントコントローラーの中身を見てきましょ。

フロントコントローラーには以下の5つの部品が存在してます!

  • DispatcherServlet
  • HandlerMapping
  • HandlerAdapter
  • ViewResolver
  • view

以下それぞれの説明さ!

DispatcherServlet
フロントコントローラーのエンドポイントとなるサーブレットクラス。司令塔的な役割を持つ。
図以外のインターフェイスを使用して他にも機能があるが、そこはひとまず割愛。

HandlerMapping
リクエストに対するcontrollerを選択する役割を持つ!
@RequestMappingアノテーションのついている元に、適切なcontrollerを探すよ。
今時は@GetMappingとかを使うかもだけど、どっちでも見つけてくれるよ!

HandlerAdapter
controllerのメソッドを呼び出す役割を持つ。
さらにメソッドの引数に値を渡す処理とかも実装されている。
さらにさらに、リクエストをjavaオブジェクトにする処理や、バリデーションとかもやっていくれる。
手広く処理してくれる頼れるやつ。

ViewResolver
controllerから返されたview名から、どのviewインターフェースの実装クラスを使うのかを決めてくれる。

view
Viewインターフェースはクライアントに返すデータを完成させるよ。

リクエストを受けてからレスポンスを返すまでの流れ

例として、リクエストがあってからレスポンスを返すまでを図で見ていくよ!

DispatcherServlet:「あ、リクエスト来た!HandlerMapping君、コントローラーをおくれ!」
HandlerMapping:「仕方ねーな。お前がそう言うなら。必殺!getHandlerメソッド!」
HandlerMapping:「そのリクエストに対するControllerだよ。どうぞ。」
DispatcherServlet:「あんがと」

DispatcherServlet:「次はcontrollerのメソッドを実行したいから、、、HandlerMapping君!!!」
HandlerMapping:「おけ。必殺!handleメソッド!」
HandlerMapping:「実行したらview名帰ってきたから、渡しとくね」
DispatcherServlet:「あざまる」

DispatcherServlet:「このview名のviewが欲しいのだが、知ってる?ViewResolver君」
ViewResolver:「知っとるよ。このviewでしょ。はい。」
DispatcherServlet:「ありがとー。じゃあこいつをレンダリングしてもらって良いですかな?Viewインターフェース君」
View:「任してくれよな。動けレンダリングエンジン!!これでレンダリングできたよ〜」
DispatcherServlet:「ありがと〜。じゃあこいつをレスポンスとして渡す〜。はいよ。」
クライアント:「ありがとね〜」

FormオブジェクトとかEntityオブジェクトとの連携!


EntityとかFormオブジェクトがどうやってviewに反映されてるんだい!って思うのでそれをこの図で紹介。フロントコントーラーが作るModelってのが。一時的な倉庫の役割をやってくれていて、そこにControllerがFormやEntityをぶち込むことによってviewはその倉庫から必要な材料を取り出しているって感じ。

DIコンテナとの繋がり

上記の例ではDispatcherServletが他の部品と連携していたけど、その部品たちは全部DIコンテナに入っておるとのこと。SpringMVCでは以下の2つのDIコンテナ(アプリケーションコンテキストとも言う)を使う。

  • WEBアプリケーション用DIコンテナ:WEBアプリに対して1つだけ作る
  • DispatchServlet用のDIコンテナ:DispatchServletに対して1つ作る

DispatchServletクラスを複数持つことってあるの?って思ったけど『API用』と『UI用(webページ用)』とかのように分けて実装することがあるとのこと。なるほどなるほど。

DispatcherServletはDispatcherServlet用のDIコンテナと繋がっておるんですな。
ちな、それぞれのDIコンテナを作るためにはConfigファイルの作成とweb.xmlに設定をする必要がある。

サーブレットコンテナとservlet、DIコンテナの繋がり

SpringMVCを始める前のセットアップ

SpringMVCを始めるにも、まずは準備から。やらなければいけないセットアップは以下の5つ。

  1. ライブラリのセットアップ
  2. ContextLoaderListenerのセットアップ
  3. DispatcherServletのセットアップ
  4. CharacterEncodingFilterのセットアップ
  5. ViewResolverのセットアップ

ライブラリのセットアップ

Marvenは以下のように。

pom.xml
<dependency>
    <groundId>org.springframework</groundId>
    <artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
    <groundId>hibernate</groundId>
    <artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
    <groundId>org.slf4j</groundId>
    <artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
    <groundId>ch.qos.logback</groundId>
    <artifactId>logback-classic</artifactId>
</dependency>

gradleならこっちで。

build.gradle
dependencies {
    compile group: 'org.springframework', name: 'spring-webmvc'
    compile group: 'hibernate', name: 'hibernate-validator<'
    compile group: 'org.slf4j', name: 'jcl-over-slf4j'
    compile group: 'ch.qos.logback', name 'logback-classic'
}

ContextLoaderListenerのセットアップ(DIコンテナを作る)

アプリケーションコンテキストを生成するためにContextLoaderListenerクラスをサーブレットコンテナに登録する必要があるとのこと。アプリケーションコンテキストとはDIコンテナのことだと思っておけば良さそうです・
また、サーブレットコンテナとはjavaのプログラムを動かすためにクライアントからの命令を中継してjavaプログラムを動かしてくれるやつってイメージ。
ContextLoaderListenerクラスをサーブレットコンテナに登録するにはまず、アプリケーションコンテキストにBeanを登録するためのBean定義が必要になる。Bean定義を行うためにはConfigクラス(設定用のクラスみたいな)を作ろう。

JavaConfig.java
@Configration
public JavaConfig {
}

このConfigクラスを使ってアプリケーションコンテキストを作るように設定します。
その設定にはweb.xmlという設定ファイルを作って書いていく必要があるとのこと。
web.xmlについてはこちらがわかりやすかった。

web.xml
<listener>
    <listener-class>
        org.springframework.web.context.ContextLoaderListener ・・・①
    </listener-class>
</listener>
<context-param>
    <param-name>contextClass</param-name>
    <param-value>
        org.springframework.web.context.support.AnnotationConfigWebApplicationConfig ・・・②
    </param-value>
</context-param>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>example.config.JavaConfig</param-value> ・・・③
</context-param>

①:サーブレットコンテナのリスナクラスとしてContextLoaderListenerを指定
②:サーブレットコンテナのcontext-paramというパラメータにAnnotationConfigWebApplicationConfigというクラスを指定する。
③:どのConfigクラスを使ってDIコンテナを作るのかをここで指定する。

DispatcherServletのセットアップ

SpringMVCのフロントコントローラーパターン(MVCの弱点を改善したアーキテクチャパターン)を利用するために、DispatcherServletクラスというものをサーブレットコンテナに登録する必要があるとのこと。それにはwebアプリケーションコンテキストとは別にDispatcherServlet用のアプリケーションコンテキストを作成しなければならないらしい。
DispatcherServlet用のアプリケーションコンテキストが必要ということはそれ用のConfigクラスを作る必要があるということ。というわけでDispatcherServlet用のConfigクラスを作ります!

@Configration
@EnableWebMvc ・・・①
@ComponentScan("example.app")
public class WebMvcConfig extends WebMvcConfigureAdapter { ・・・②
}

①:@EnableWebMvcを指定することでSpringMVCが提供するコンフィギュレーションクラスがインポートされ、SpringMVCを利用するのに必要となるコンポーネントのBean定義が自動で行われる。
②:WebMvcConfigureAdapterクラスを継承することで、デフォルトで提供されるBean定義を簡単にカスタマイズできるようになる。

続いて、DispatcherServletクラスをサーブレットコンテナに登録します。その際に、先ほど作成したDispatcherServlet用のConfigクラスを使ってアプリケーションコンテキスト(DIコンテナ)を生成するように定義します。

web.xml
<servlet>
    <servlet-name>app</servlet-name>
    <servlet-class>
        org.springframework.web.servlet.DispatcherServlet ・・・①
    </servlet-class>
    <init-param>
        <param-name>contextClass</param-name>
        <param-value>
            org.springframework.web.context.support.AnnotationCofigWebApplicationContect ・・・②
        </param-value>
    </init-param>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>example.config.WebConfig</param-value> ・・・③
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>app</servlet-name>
    <url-pattern>/</url-pattern> ・・・④
</servlet-mapping>

①:フロントコントローラー(全部の処理を一旦受け取るコントローラー)を設定するタブの<servlet-class>DispatcherServletクラスを登録する。こいつはspringが提供してくれているクラスで、とりあえずこいつを使っておけばいいらしい。このクラスに好きな名前をつけるのだが、今回はappにしよう(上の<servlet-name>に名前を入力)。
②:contextClassパラメータにAnnotationCofigWebApplicationContectを指定。こいつは何か調べてみると、@ConfigrationアノテーションのついたConfigクラスをアプリケーションコンテナを作る時に利用します!っていう設定とのこと。参考
③:ハンドリングするURLのパターンを指定し、ハンドリングしたリクエストをどのサーブレットクラスで捌いてもらうかを<servlet-name>タグで指定。ここではwebアプリに対する全てのリクエストをappと言う名前のクラスで捌くように指示している。appの名前がついているのは①のDispatcherServletなので、こいつに渡すことになる。

ViewResolverのセットアップ

SpringMVCではビュー名を解決して使用するViewを判別するためにViewResolverを使うらしい。
ここではviewにJSPを使うものとしてViewResolverの実装手順を書いていく。

@Configuration
@EnableWebMvc
@ComponentScan("example.app")
public class WebConfig extends WebMvcConfigAdapter {
    @Override
    public void configureViewResolvers(viewResolverRegistry registry) { ・・・①
        registry.jsp(); ・・・②
    }
}

①:configureViewResolversのメソッドをオーバーライド
②:viewResolverRegistryクラスのjspメソッドを呼び出しJSP用のviewResolverをセットアップする。

これでセットアップは終わりです!お疲れ様です!

用語集

  • メソッドシグネチャ:「メソッド名」「パラメータ数と順序、パラメータの型」「戻り値の型」をまとめた名称
  • サーブレット:webサーバーで働くjavaのアプリケーションのこと。ここがわかりやすい。
  • プレゼンテーションロジック:ユーザーが向き合う画面のロジック
  • ビジネスロジック:データ処理を行うところ
  • WARファイル:http://e-words.jp/w/WAR%E5%BD%A2%E5%BC%8F.html
  • Handler:controllerのこと。