はじめてのSpring MVCアプリケーション -------------------------------------------------------------- .. only:: html .. contents:: 目次 :depth: 3 :local: Spring MVCの、詳細な使い方の解説に入る前に、実際にSpring MVCに触れることで、 Spring MVCを用いたWebアプリケーションの開発に対するイメージをつかむ。 本節は、全体イメージをつかむことを目的としており、 **次章以降で説明する推奨方式に従っていないことに注意する。** 検証環境 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 本節の説明では、次の環境で動作検証している。(他の環境で実施する際は、本書をベースに適宜読み替えて設定していくこと。) .. tabularcolumns:: |p{0.75\linewidth}|p{0.25\linewidth}| .. list-table:: :header-rows: 1 :widths: 75 25 * - Product - Version * - JDK - 1.7.0\_55 * - SpringSource Tool Suite (STS) - 3.5.0.RELEASE * - VMware vFabric tc Server Developer Edition - 2.9 * - Fire Fox - ESR 24 .. note:: インターネット接続するために、プロキシサーバーを介する必要がある場合、 以下の作業を行うため、STSのProxy設定と、 `MavenのProxy設定 `_\ が必要である。 新規プロジェクト作成 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ インターネットから `mvn archetype:generate` を利用して、プロジェクトを作成する。 .. code-block:: console mvn archetype:generate -B^ -DarchetypeCatalog=http://repo.terasoluna.org/nexus/content/repositories/terasoluna-gfw-releases^ -DarchetypeGroupId=org.terasoluna.gfw.blank^ -DarchetypeArtifactId=terasoluna-gfw-web-blank-archetype^ -DarchetypeVersion=1.0.1.RELEASE^ -DgroupId=com.example.helloworld^ -DartifactId=helloworld^ -Dversion=1.0.0-SNAPSHOT ここではwindows上にプロジェクトの元を作成する。 .. code-block:: console C:\work>mvn archetype:generate -B^ More? -DarchetypeCatalog=http://repo.terasoluna.org/nexus/content/repositories/terasoluna-gfw-releases^ More? -DarchetypeGroupId=org.terasoluna.gfw.blank^ More? -DarchetypeArtifactId=terasoluna-gfw-web-blank-archetype^ More? -DarchetypeVersion=1.0.1.RELEASE^ More? -DgroupId=com.example.helloworld^ More? -DartifactId=helloworld^ More? -Dversion=1.0.0-SNAPSHOT [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building Maven Stub Project (No POM) 1 [INFO] ------------------------------------------------------------------------ [INFO] [INFO] >>> maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom >>> [INFO] [INFO] <<< maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom <<< [INFO] [INFO] --- maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom --- [INFO] Generating project in Batch mode [INFO] Archetype repository missing. Using the one from [org.terasoluna.gfw.blank:terasoluna-gfw-web-blank-archetype:1.0.0.RELEASE -> http://repo.terasoluna.org/nexus/content/repositories/terasoluna-gfw-releases] found in catalog http://repo.terasoluna.org/nexus/content/repositories/terasoluna-gfw-releases [INFO] ---------------------------------------------------------------------------- [INFO] Using following parameters for creating project from Archetype: terasoluna-gfw-web-blank-archetype:1.0.1.RELEASE [INFO] ---------------------------------------------------------------------------- [INFO] Parameter: groupId, Value: com.example.helloworld [INFO] Parameter: artifactId, Value: helloworld [INFO] Parameter: version, Value: 1.0.0-SNAPSHOT [INFO] Parameter: package, Value: com.example.helloworld [INFO] Parameter: packageInPathFormat, Value: com/example/helloworld [INFO] Parameter: package, Value: com.example.helloworld [INFO] Parameter: version, Value: 1.0.0-SNAPSHOT [INFO] Parameter: groupId, Value: com.example.helloworld [INFO] Parameter: artifactId, Value: helloworld [INFO] project created from Archetype in dir: C:\work\helloworld [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 6.047s [INFO] Finished at: Fri Aug 22 14:51:57 JST 2014 [INFO] Final Memory: 14M/182M [INFO] ------------------------------------------------------------------------ C:\work> SpringSource Tool Suiteのメニューから、[File] -> [Import] -> [Maven] -> [Existing Maven Projects] -> [Next]を選択し、archetypeで作成したプロジェクトを選択する。 .. figure:: images/NewMVCProjectImport.png :alt: New MVC Project Import :width: 60% Root Directoryに \ ``C:\work\helloworld``\ を設定し、Projectsにhelloworldのpom.xmlが選択された状態で、 [Finish] を押下する。 .. figure:: images/NewMVCProjectCreate.png :alt: New MVC Project Import :width: 60% Package Explorerに、次のようなプロジェクトが生成される( **要インターネット接続** )。 .. figure:: images/HelloWorldWorkspace.png :alt: workspace Spring MVCの設定方法を理解するために、生成されたSpring MVCの設定ファイル(src/main/resources/META-INF/spring/spring-mvc.xml)について、簡単に説明する。 .. code-block:: xml :emphasize-lines: 15-16, 25-26, 58-64 .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - \ ````\要素を定義することにより、Spring MVCのデフォルト設定が行われる。デフォルトの設定については、 Springの公式ページである `Enabling the MVC Java Config or the MVC XML Namespace `_ を参照されたい。 * - | (2) - Spring MVCで使用するコンポーネントを探すパッケージを定義する。 * - | (3) - ViewのResolverを指定し、Viewの配置場所を定義する。 ``com.example.helloworld.app.welcome.HomeController`` を以下に示す。 .. code-block:: java :emphasize-lines: 17,26,36,38 package com.example.helloworld.app.welcome; import java.text.DateFormat; import java.util.Date; import java.util.Locale; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; /** * Handles requests for the application home page. */ @Controller // (1) public class HomeController { private static final Logger logger = LoggerFactory .getLogger(HomeController.class); /** * Simply selects the home view to render by returning its name. */ @RequestMapping(value = "/", method = {RequestMethod.GET, RequestMethod.POST}) // (2) public String home(Locale locale, Model model) { logger.info("Welcome home! The client locale is {}.", locale); Date date = new Date(); DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale); String formattedDate = dateFormat.format(date); model.addAttribute("serverTime", formattedDate); // (3) return "welcome/home"; // (4) } } 簡単に、解説を行う。 .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - ``@Controller`` アノテーションを付けることで、DIコンテナにより、コントローラクラスが自動で読み込まれる。前述「Spring MVCの設定ファイルの説明(2)」の設定により、component-scanの対象となっている。 * - | (2) - HTTPメソッドがGETで、Resource(もしくはRequest URL)が"/"で、アクセスする際に実行される。 * - | (3) - Viewに渡したいオブジェクトを設定する。 * - | (4) - View名を返却する。前述「Spring MVCの設定ファイルの説明(3)」の設定により、"WEB-INF/views/home.jsp"がレンダリングされる。 Modelに設定したオブジェクトが、HttpServletRequestに設定される。 home.jspで以下のように ``${serverTime}`` を記述することで、Controllerから渡された値を出力することができる。 ( **ただし、${XXX}の記述は、XSS対象になる可能性があるので、文字列を出力する場合は、後述のようにHTMLエスケープする必要があることに注意する。** ) .. code-block:: jsp Home

Hello world!

The time on the server is ${serverTime}.

| サーバーを起動する ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | STSで、"helloworld"プロジェクトを右クリックして、"Run As" -> "Run On Server" -> "localhost" -> "VMware vFabric tc Server Developer Edition v2.9" -> "Finish"を実行し、helloworldプロジェクトを起動する。 | ブラウザに "http://localhost:8080/helloworld/" を入力し、実行すると下記の画面が表示される。 .. figure:: images/AppHelloWorldIndex.png :alt: Hello World | .. _first-application-create-an-echo-application: エコーアプリケーションの作成 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 続いて、簡単なアプリケーションを作成する。作成するのは、次の図のようなテキストフィールドに、名前を入力すると メッセージを表示する、いわゆるエコーアプリケーションである。 .. figure:: images/AppEchoIndex.png :alt: Form of Echo Application .. figure:: images/AppEchoHello.png :alt: Output of Echo Application | フォームオブジェクトの作成 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | まずは、テキストフィールドの値を受け取るための、フォームオブジェクトを作成する。 | \ ``com.example.helloworld.app.echo``\ パッケージに\ ``EchoForm``\ クラスを作成する。プロパティを1つだけ持つ、単純なJavaBeanである。 .. code-block:: java package com.example.helloworld.app.echo; import java.io.Serializable; public class EchoForm implements Serializable { private static final long serialVersionUID = 2557725707095364445L; private String name; public void setName(String name) { this.name = name; } public String getName() { return name; } } | Controllerの作成 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | 次に、Controllerを作成する。 | 同じく ``com.example.helloworld.app.echo`` パッケージに、``EchoController`` クラスを作成する。 .. code-block:: java :emphasize-lines: 12,18,20,23-25 package com.example.helloworld.app.echo; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("echo") public class EchoController { @ModelAttribute // (1) public EchoForm setUpEchoForm() { EchoForm form = new EchoForm(); return form; } @RequestMapping // (2) public String index(Model model) { return "echo/index"; // (3) } @RequestMapping("hello") // (4) public String hello(EchoForm form, Model model) {// (5) model.addAttribute("name", form.getName()); // (6) return "echo/hello"; } } .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | ``@ModelAttribute`` というアノテーションを、メソッドに付加する。このアノテーションがついたメソッドの返り値は、自動でModelに追加される。 | Modelの属性名を、 ``@ModelAttribute`` で指定することもできるが、デフォルトでは、クラス名の先頭を小文字にした値が、属性名になる。この場合は、”echoForm”である。フォームの属性名は、次に説明する ``form:form タグ`` の ``modelAttribute`` 属性の値に一致している必要がある。 * - | (2) - | メソッドに付加した ``@RequestMapping`` アノテーションの ``value`` 属性に、何も指定しない場合、クラスに付加した ``@RequestMapping`` のルートに、マッピングされる。この場合、"/echo"にアクセスすると、 ``index`` メソッドが呼ばれる。 | ``method`` 属性に何もしない場合は、任意のHTTPメソッドでマッピングされる。 * - | (3) - | View名で"echo/index"を返すので、ViewResolverにより、 "WEB-INF/views/echo/index.jsp"がレンダリングされる。 * - | (4) - | メソッドに付加した ``@RequestMapping`` アノテーションに"hello"を指定しているので、この場合、"/echo/hello"にアクセスすると ``hello`` メソッドが呼ばれる。 * - | (5) - | 引数に、EchoFormには(1)によりModelに追加されたEchoFormオブジェクトが渡される。 * - | (6) - | フォームで入力された ``name`` を、Viewにそのまま渡す。 | JSPの作成 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 最後に、入力画面と、出力画面のJSPを作成する。それぞれのファイルパスは、View名に合わせて、次のようになる。 * 入力画面 src/main/webapp/WEB-INF/views/echo/index.jsp .. code-block:: jsp Echo Application Input Your Name: .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | タグライブラリを利用し、HTMLフォームを構築している。 ``modelAttribute`` 属性に、Controllerで用意したフォームオブジェクトの名前を指定する。 | タグライブラリは `こちら `_\を参照されたい。 出力されるHTMLは、 .. code-block:: html Echo Application
となる。 * 出力画面 src/main/webapp/WEB-INF/views/echo/hello.jsp .. code-block:: jsp Echo Application

Hello !

.. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (2) - | Controllerから渡された"name"を出力する。 ``c:out`` タグにより、XSS対策を行っている。 .. note:: ここではXSS対策を標準タグの ``c:out`` で実現したが、より容易に使用できる ``f:h()`` 関数を共通ライブラリで用意している。 詳細は、 :doc:`../Security/XSS` を参照されたい。 | | これでエコーアプリケーションの実装は完了である。 | サーバーを起動し、 "http://localhost:8080/helloworld/echo"にアクセスするとフォームが表示される。 | 入力チェックの実装 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ここまでのアプリケーションでは、入力チェックを行っていない。 Spring MVCでは、Bean Validation (`JSR-303 `_\)をサポートしており、アノテーションベースな入力チェックを、簡単に 実装することができる。例として、エコーアプリケーションで名前の入力チェックを行う。 ``EchoForm`` を以下の通り、 ``name`` プロパティに ``@NotNull`` アノテーションおよび、 ``@Size`` アノテーションを付加する(getterメソッドに付加してもよい)。 .. code-block:: java :emphasize-lines: 5,6,11,12 package com.example.helloworld.app.echo; import java.io.Serializable; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; public class EchoForm implements Serializable { private static final long serialVersionUID = 2557725707095364445L; @NotNull // (1) @Size(min = 1, max = 5) // (2) private String name; public void setName(String name) { this.name = name; } public String getName() { return name; } } .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | ``@NotNull`` アノテーションをつけることで、HTTPリクエスト中に ``name`` パラメータがあることを確認する。 * - | (2) - | ``@Size(min = 1, max = 5)`` をつけることで、``name`` のサイズが、1以上5以下であることを確認する。 ``EchoController`` を以下の通り、 ``@Valid`` アノテーションを追加し、 ``hasErrors`` メソッドを追加する。 .. code-block:: java :emphasize-lines: 5,6,26-29 package com.example.helloworld.app.echo; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("echo") public class EchoController { @ModelAttribute public EchoForm setUpEchoForm() { EchoForm form = new EchoForm(); return form; } @RequestMapping public String index(Model model) { return "echo/index"; } @RequestMapping("hello") public String hello(@Validated EchoForm form, BindingResult result, Model model) { // (1) if (result.hasErrors()) { // (2) return "echo/index"; } model.addAttribute("name", form.getName()); return "echo/hello"; } } .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | コントローラー側には、Validation対象の引数に ``@Validated`` アノテーションを付加し、 ``BindingResult`` オブジェクトを引数に追加する。 | Bean Validationによる入力チェックは、自動で行われる。結果は、 ``BindingResult`` オブジェクトに渡される。 * - | (2) - | ``hasErrors`` メソッドを実行して、エラーがあるかどうかを確認できる。 入力画面 src/main/webapp/WEB-INF/views/echo/index.jsp に、 ``form:errors`` タグを追加する。 .. code-block:: jsp :emphasize-lines: 15 Echo Application Input Your Name: .. tabularcolumns:: |p{0.10\linewidth}|p{0.90\linewidth}| .. list-table:: :header-rows: 1 :widths: 10 90 * - 項番 - 説明 * - | (1) - | 入力画面には、エラーがあった場合に、エラーメッセージを表示するため、 ``form:errors`` タグを追加する。 | 以上で、入力チェックの実装は完了である。 | 実際に、次のような場合、エラーメッセージが表示される。 * 名前を空にして送信した場合 * 5文字より大きいサイズで送信した場合 .. figure:: images/AppValidationEmpty.png :alt: Validation Error (name is empty) .. figure:: images/AppValidationSizeOver.png :alt: Validation Error (name's size is over 5) 出力されるHTMLは、 .. code-block:: html Echo Application
size must be between 1 and 5
となる。 まとめ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ この章では、 #. \ ``mvn archetype:generate``\を利用したブランクプロジェクトの作成方法 #. SpringMVCの基本的な設定方法 #. 最も簡易な、画面遷移方法 #. 画面間での値の引き渡し方法 #. シンプルな入力チェック方法 を学んだ。 上記の内容が理解できていない場合は、もう一度、本節を読み、環境構築から始めて、進めていくことで理解が深まる。 .. raw:: latex \newpage