Spring Web MVC を使ってみる

Spring Web MVC ( Spring MVC ) を使ってみたので、メモしておきます。

今回は、以下のドメインクラスに対してデータの検索 ( id 条件にして検索 / 全件検索 )、データ追加するって感じでサンプルアプリっぽいものを作ってみました。

■ Person.java

package com.example.domain;

public class Person {
	
	public Person(int id, String name) {
		this.id = id;
		this.name = name;
	}
	
	private int id;
	private String name;
 // - getter / setter 省略 -
}


では、順にいきます。

1. pom.xml の作成


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>spring-app</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>
  
  <dependencies>
    <dependency>
	  <groupId>org.springframework</groupId>
	  <artifactId>spring-context</artifactId>
	  <version>3.2.13.RELEASE</version>
    </dependency>
    <dependency>
	  <groupId>org.springframework</groupId>
	  <artifactId>spring-web</artifactId>
	  <version>3.2.13.RELEASE</version>
    </dependency>
    <dependency>
	  <groupId>org.springframework</groupId>
	  <artifactId>spring-webmvc</artifactId>
	  <version>3.2.13.RELEASE</version>
    </dependency>
    
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
  </dependencies>
  
</project>

今回はビューを JSP にしたので、jstl のライブラリも依存関係に含めておく。


2. web.xml の作成


<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

  <servlet>
    <servlet-name>mvcexample</servlet-name>
     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
     <init-param>
       <param-name>contextConfigLocation</param-name>
       <param-value>/WEB-INF/applicationContext.xml</param-value>
     </init-param>
     <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>mvcexample</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

Spring MVC のフロントコントローラになる DispatcherServlet の設定を web.xml にする。

bean 定義ファイル ( applicationContext.xml ) の設定を contextConfigLocation にしておく。
デフォルトだと "[servlet-name]-servlet.xml" を探しにいくっぽいので、上記の例だと mvcexample-servlet.xml であれば contextConfigLocation の設定は不要なのかな。

・17. Web MVC framework
- 17.2 The DispatcherServlet
http://docs.spring.io/spring/docs/3.2.13.RELEASE/spring-framework-reference/html/mvc.html

※ web.xml 以外でも WebApplicationInitializer の実装クラスで設定する方法もあるんですね。


3. サービスクラスの作成



■ PersonService.java

package com.example.service;

import java.util.List;

import com.example.domain.Person;
import com.example.exception.PersonNotFoundException;

public interface PersonService {
	public Person getPerson(int i) throws PersonNotFoundException;
	public List<Person> getAllPerson();
	public void addPerson(Person p);
}

■ PersonServiceDAOImpl.java

package com.example.service;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Component;

import com.example.domain.Person;
import com.example.exception.PersonNotFoundException;

@Component
public class PersonServiceDAOImpl implements PersonService {

	private List<Person> persons = new ArrayList<Person>();

	public PersonServiceDAOImpl() {
		persons.add(new Person(1, "hoge"));
		persons.add(new Person(2, "uga"));
	}
	
	@Override
	public Person getPerson(int i) throws PersonNotFoundException {
		for(Person p : persons) {
			if(p.getId() == i) {
				return p;
			}
		}
		throw new PersonNotFoundException();
	}

	@Override
	public List<Person> getAllPerson() {
		return persons;
	}

	@Override
	public void addPerson(Person p) {
		persons.add(p);
	}
}

特に変わったことはしてませんが、今回は簡単なサンプルでデータベース使わないので、データ保持する変数として persons を定義しております。PersonNotFoundException は Exception 継承しただけの例外クラスです。


4. コントローラクラスの作成



■ PersonController.java

package com.example.mvc;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.example.domain.Person;
import com.example.exception.PersonNotFoundException;
import com.example.service.PersonService;

@Controller
public class PersonController {

	private PersonService pservice;

	@Autowired
	public PersonController(PersonService pservice) {
		this.pservice = pservice;
	}

	@RequestMapping(value = "/getPerson", method = RequestMethod.GET)
	public String getPerson(Model model, @RequestParam("id") int id) throws PersonNotFoundException {
		model.addAttribute("person", pservice.getPerson(id));
		return "person";
	}

	@RequestMapping(value = "/getAllPerson", method = RequestMethod.GET)
	public String getAllPerson(Model model) {
		model.addAttribute("allPerson", pservice.getAllPerson());
		return "allPerson";
	}

	@RequestMapping(value = "/addPerson", method = RequestMethod.POST)
	public String addPerson(Model model, @RequestParam("id") int id, @RequestParam("name") String name) {
		pservice.addPerson(new Person(id, name));
		return "addPerson";
	}

	@ExceptionHandler(PersonNotFoundException.class)
	public String handlePersonNotFoundException() {
		return "error";
	}
}

Spring MVC に関連する箇所としては、@Controller、@RequestMapping、@RequestParam あたりのアノテーションでしょうか。
@Controller はコントローラクラスであることを示す、@RequestMapping は URL とメソッドマッピングしてくれる、@RequestParam はリクエストパラメータをメソッドの引数にバインドしてくれるアノテーションって感じです。ドキュメント的には「17.3.1 Defining a controller with @Controller」、「17.3.2 Mapping Requests With @RequestMapping」あたりになります。

@ExceptionHandler 付けたメソッドを定義していると、コントローラクラスのメソッドで該当する例外が発生した場合に、処理 ( 上記の例では error のビュー名を返す ) を実行してくれるみたいです。


5. bean 定義ファイルの作成


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:mvc="http://www.springframework.org/schema/mvc"
  xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">
	
  <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <property name="suffix" value=".jsp"/>
  </bean>

  <mvc:resources location="/WEB-INF/html/" mapping="/html/**"/>
  
  <mvc:annotation-driven />
  <context:annotation-config />
  <context:component-scan base-package="com.example.*" />
</beans>


6. JSP ファイルの作成



■ person.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
id : <c:out value="${person.id}" /><br>
name : <c:out value="${person.name}" /><br>
</body>
</html>

■ allPerson.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<c:forEach var="p" items="${allPerson}">
id : <c:out value="${p.id}" /><br>
name : <c:out value="${p.name}" /><br>
</c:forEach>
</body>
</html>

■ addPerson.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<h2>Add Person OK</h2>
</body>
</html>

error.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<h2>Not Found Person</h2>
</body>
</html>

以下はフロントページです。

■ index.html

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<form action="/spring-app/getPerson" method="get">
<h2>Get Person</h2>
id : <input type="text" name="id" />
<input type="submit" value="getPerson" /><br>
</form>
<hr>
<form action="/spring-app/getAllPerson" method="get">
<h2>Get All Person</h2>
<input type="submit" value="getAllPerson" /><br>
</form>
<hr>
<form action="/spring-app/addPerson" method="post">
<h2>Add Person</h2>
id : <input type="text" name="id" /><br>
name : <input type="text" name="name" />
<input type="submit" value="getAllPerson" /><br>
</form>
<hr>
</body>
</html>

JSP ファイルは WEB-INF/jsp 配下に、HTML ファイルは WEB-INF/html 配下におきました。


上記でやることは以上です。あとは、Tomcat なりの AP サーバにデプロイするとなんとなく動くと思います。

入力値のチェックとかもしてないですし、不足点いっぱいありますが、ひとまず今回はこれぐらいで。


[ 環境情報 ]
Spring 3.2.13.RELEASE
Java SE 8 Update 25
Apache Tomcat 8.0.21