Spring Boot 使い方メモ

# Spring Boot とは
- Spring プロジェクトが提供する様々なライブラリやフレームワークを、手っ取り早く使えるようにしたフレームワーク。
- Dropwizard の Spring 版みたいなの。
- ビルドすると単独の jar ファイルができあがる。
    - Web アプリの場合は、組み込みの Tomcat が起動する（Jetty や Undertow に切り替え可）。
    - Web アプリでなく、普通の Java プログラムとしても動かせる。
- Maven や Gradle などのビルドツールを利用する（Ant でも[できなくはない][1]）。
    - 使用したいコンポーネントを依存関係に追加するだけで、結合に必要な設定などが自動で行われる。

# 環境
## Java
- 1.8.0_45

## Gradle
- 2.3

## Spring Boot
- 1.2.3

# Hello World
## 実装
```groovy:build.gradle
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.2.3.RELEASE'
    }
}

apply plugin: 'java'
apply plugin: 'spring-boot' // 新しいバージョンのプラグインを使うと、 'spring-boot' ではなく 'org.springframework.boot' を使ってと警告が出るので注意

sourceCompatibility = '1.8'
targetCompatibility = '1.8'

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.springframework.boot:spring-boot-starter'
}

jar.baseName = 'spring-boot-sample'
```

```java:Main.java
package sample.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.hello();
        }
    }
    
    public void hello() {
        System.out.println("Hello Spring Boot!!");
    }
}
```

## Gradle から実行
```bash:
$ gradle bootRun
:compileJava
:processResources UP-TO-DATE
:classes
:findMainClass
:bootRun

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.2.3.RELEASE)

（略）

Hello Spring Boot!!

（略）

2015-04-29 12:45:26.542  INFO 5792 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown

BUILD SUCCESSFUL

Total time: 4.639 secs
```

## ビルドして実行
```bash:
$ gradle build

$ java -jar build/libs/spring-boot-sample.jar

（略）

Hello Spring Boot!!

（略）
```

## 説明
### build.gradle の設定
Spring Boot 用の Gradle プラグインが用意されているので、最初にそれを読み込む。

```groovy:build.gradle
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.2.3.RELEASE'
    }
}

apply plugin: 'spring-boot'
```

そして、通常の Java プログラムを作るだけなら、 `spring-boot-starter` を依存関係に追加する。

```groovy:build.gradle
repositories {
    mavenCentral()
}

dependencies {
    compile 'org.springframework.boot:spring-boot-starter'
}
```

### Spring Boot の起動
Spring Boot の起動には、 `SpringApplication` クラスを使う。

最も単純な方法が、 `SpringApplication#run(Object, String...)` を使う方法。

```java:Main.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.hello();
        }
    }
    
    public void hello() {
        System.out.println("Hello Spring Boot!!");
    }
}
```

- `run()` メソッドの第一引数には、 `@EnableAutoConfiguration` でアノテートしたクラスの `Class` オブジェクトを渡す。
    - `Main` クラスは `@SpringBootApplication` でアノテートされているが、これは `@Configuration`, `@EnableAutoConfiguration`, `@ComponentScan` の３つでクラスをアノテートしたのと同じ扱いになる。
    - `@Configuration` は、 Spring の色々な設定を Java コード上で行えるようにするためのアノテーション。
        - 昔の Spring は XML で設定を書いていたが、今は Java コード上で設定を行うのが主流になっているっぽい。
    - `@EnableAutoConfiguration` は、 Spring の設定を自動化するためのアノテーション。
        - これがあることで、依存関係を追加するだけで Spring MVC などのライブラリを設定記述なしで使えるようになる。
    - `@ComponentScan` は、 DI コンテナが管理する Bean を自動登録するためのアノテーション。
        - これでアノテートされたクラスを起点として、配下のパッケージを再帰的にスキャンして、 `@Component` でアノテートされたクラスを Bean としてコンテナに登録する。
    - この３つはだいたい一緒に使うことが多いので、 `@SpringBootApplication` を使うと少し楽になる。
- 第二引数には、コマンドラインの引数を渡す。

### Gradle での実行・ビルド
- アプリケーションの起動は、 spring-boot-gradle-plugin が提供する `bootRun` タスクを使用する。
- jar の作成は、普通に `build` タスクで OK。
- 作成した jar は、普通に `jar -jar <jarファイル>` で実行できる。

# Java コード上で Bean を定義する
CDI で言うところの Provider 的なやつ。

## 基本
```java:Hoge.java
package sample.springboot;

public class Hoge {
    
    private String name;
    
    public Hoge(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Hoge [name=" + name + "]";
    }
}
```

```java:Main.java
package sample.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Hoge h = ctx.getBean(Hoge.class);
            System.out.println(h);
        }
    }
    
    @Bean
    public Hoge getHoge() {
        System.out.println("Main#getHoge()");
        return new Hoge("hoge");
    }
}
```

```text:実行結果
Main#getHoge()
Hoge [name=hoge]
```

- `@Bean` でメソッドをアノテートすると、そのメソッドを通じて Bean のインスタンスを生成できるようになる。
- このような Bean を定義するメソッドは、 `@Configuration` でアノテートしたクラスに宣言できる。
    - `@SpringBootApplication` は `@Configuration` でアノテートしたのと同じ効果がある。

## @Configuration でアノテートしたクラスを別途作成する
```java:HogeProvider.java
package sample.springboot;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class HogeProvider {
    
    @Bean
    public Hoge getHoge() {
        System.out.println("HogeProvider#getHoge()");
        return new Hoge("hoge provider");
    }
}
```

```java:Main.java
package sample.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Hoge h = ctx.getBean(Hoge.class);
            System.out.println(h);
        }
    }
}
```

```text:実行結果
HogeProvider#getHoge()
Hoge [name=hoge provider]
```

- `@Configuration` でクラスをアノテートし、 `@Bean` でメソッドをアノテートすれば、任意のクラスで Bean を生成するメソッドを定義できる。

# Web アプリを作る
## Hello World
### 実装
```diff:build.gradle
dependencies {
-   compile 'org.springframework.boot:spring-boot-starter'
+   compile 'org.springframework.boot:spring-boot-starter-web'
}
```

```java:Main.java
package sample.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }
}
```

```java:HelloController.java
package sample.springboot.web;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/hello")
public class HelloController {
    
    @RequestMapping(method=RequestMethod.GET)
    public String hello() {
        return "Hello Spring MVC";
    }
}
```

### 動作確認
```bash:起動
$ gradle bootRun
（略）
2015-04-29 18:29:29.317  INFO 5772 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2015-04-29 18:29:29.318  INFO 5772 --- [           main] sample.springboot.Main                   : Started Main in 2.244 seconds (JVM running for 2.531)
> Building 80% > :bootRun
```

```bash:curlでテスト
$ curl http://localhost:8080/hello
Hello Spring MVC
```

### 説明
#### Web アプリ用の依存関係
```diff:build.gradle
dependencies {
-   compile 'org.springframework.boot:spring-boot-starter'
+   compile 'org.springframework.boot:spring-boot-starter-web'
}
```

- Web アプリを作る場合は、 `spring-boot-starter-web` モジュールを使用する。
- デフォルトでは、 Spring MVC を使って Web アプリを作ることになる。

#### 起動方法の変更
```diff:Main.java
    public static void main(String[] args) {
-       try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
-           ....
-       }
+       SpringApplication.run(Main.class, args);
```

- サーバー起動後にコンテナがシャットダウンしてしまうので、try-with-resources 文は使わないように変更する。

#### Spring MVC のコントローラクラス
```java:HelloController.java
@RestController
@RequestMapping("/hello")
public class HelloController {
    
    @RequestMapping(method=RequestMethod.GET)
    public String hello() {
        return "Hello Spring MVC";
    }
}
```

- Web API のエントリポイントとなるクラスを作る場合は、 `@RestController` でクラスをアノテートする。
    - Web API ではなく、 MVC の C となるコントローラにしたい場合は `@Controller` でアノテートする（[詳細後述](#テンプレートエンジンを利用する)）。
- `@RequestMapping` で、パスや HTTP メソッドのマッピングをする（だいたい JAX-RS と同じノリ）。

## サーバーのポート番号を変更する
```properties:application.properties
server.port=1598
```

```bash:動作確認
$ gradle bootRun
（略）

2015-05-02 00:09:11.201  INFO 5968 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 1598 (http)

（略）
```

- `server.port` でポート番号を指定できる。
- `application.properties` についての説明は [こちら](#外部設定externalized-configurationを利用する)。
- 他にも以下のような変更ができる。
    - `server.address` ：リスアンドレス（`localhost` にすれば、ローカルからしかアクセスできなくなる）。
    - `server.sessionTimeout` ：セッションタイムアウト時間。

## リクエストとレスポンスのマッピング
```java:Hoge.java
package sample.springboot.web;

public class Hoge {
    
    public int id;
    public String value;
    
    @Override
    public String toString() {
        return "Hoge [id=" + id + ", value=" + value + "]";
    }
}
```

```java:HelloController.java
package sample.springboot.web;

import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/hello")
public class HelloController {
    
    @RequestMapping(method=RequestMethod.POST)
    public Hoge hello(@RequestBody Hoge param) {
        System.out.println(param);
        
        Hoge hoge = new Hoge();
        hoge.id = 20;
        hoge.value = "Response";
        
        return hoge;
    }
}
```

```bash:動作確認
$ curl -H "Content-type: application/json" -X POST -d '{"id": 10, "value": "Request"}' http://localhost:8080/hello
{"id":20,"value":"Response"}
```

```text:サーバーコンソール出力
Hoge [id=10, value=Request]
```

- デフォルトでは、リクエスト・レスポンスともに JSON によるマッピングが有効になっている。
- マッピングは Jackson がやっている（なので、マッピングの調整は Jackson のアノテーションでできる）。

## Spring MVC の簡単な使い方メモ
### URL のマッピング
```java:HelloController.java
package sample.springboot.web;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/hello")
public class HelloController {
    
    @RequestMapping(method=RequestMethod.GET)
    public String getMethod() {
        return "get";
    }
    
    @RequestMapping(method=RequestMethod.POST)
    public String postMethod1() {
        return "post";
    }
    
    @RequestMapping(value="/hey", method=RequestMethod.POST)
    public String postMethod2() {
        return "hey post";
    }
}
```

```bash:動作確認
$ curl http://localhost:8080/hello
get

$ curl http://localhost:8080/hello -X POST
post

$ curl http://localhost:8080/hello/hey -X POST
hey post
```

- `@RequestMapping` でメソッド（クラス）とパスをマッピングする。
- `value` 属性にパスを指定する。
- `method` 属性に、 HTTP メソッドを指定する。

### パスパラメータの取得
```java:HelloController.java
package sample.springboot.web;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/hello")
public class HelloController {
    
    @RequestMapping(value="/{id}/{name}", method=RequestMethod.GET)
    public void getMethod(@PathVariable int id, @PathVariable String name) {
        System.out.println("id=" + id + ", name=" + name);
    }
}
```

```bash:動作確認
$ curl http://localhost:8080/hello/100/hoge
```

```text:サーバーコンソール出力
id=100, name=hoge
```

- パスの定義に波括弧（`{}`）で括ったパラメータを定義し、メソッドのパラメータに同名の引数を定義して `@PathVariable` でアノテートする。

### クエリパラメータの取得
```java:HelloController.java
package sample.springboot.web;

import java.util.Map;

import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/hello")
public class HelloController {
    
    @RequestMapping(method=RequestMethod.GET)
    public void getMethod(
            @RequestParam String id,
            @RequestParam Map<String, String> queryParameters,
            @RequestParam MultiValueMap<String, String> multiMap) {
        
        System.out.println("id=" + id);
        System.out.println(queryParameters);
        System.out.println(multiMap);
    }
}
```

```bash:動作確認
$ curl "http://localhost:8080/hello?id=100&name=hoge&name=fuga"
```

```text:サーバーコンソール出力
id=100
{id=100, name=hoge}
{id=[100], name=[hoge, fuga]}
```

- `@RequestParam` でメソッドの引数をアノテートすることで、クエリパラメータを取得できる。
- 引数の型が `Map` の場合は、クエリパラメータの情報を `Map` 形式で取得できる。
- １つのパラメータに複数の値が設定されている場合は、 Spring が提供する `MultiValueMap` で受け取ることができる。

### リクエストヘッダーを取得する
```java:HelloController.java
package sample.springboot.web;

import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/hello")
public class HelloController {
    
    @RequestMapping(method=RequestMethod.GET)
    public void getMethod(@RequestHeader("Test-Header") String value) {
        System.out.println("Test-Header=" + value);
    }
}
```

```bash:動作確認
$ curl -H "Test-Header: hoge" http://localhost:8080/hello
```

```text:サーバーコンソール出力
Test-Header=hoge
```

- `@RequestHeader` でヘッダー情報を取得できる。

### リクエストボディの値を取得する
```java:HelloController.java
package sample.springboot.web;

import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/hello")
public class HelloController {
    
    @RequestMapping(method=RequestMethod.POST)
    public void getMethod(@RequestBody String body) {
        System.out.println("body=" + body);
    }
}
```

```bash:動作確認
$ curl http://localhost:8080/hello -X POST -d "Request Body"
```

```text:サーバーコンソール出力
body=Request+Body=
```

- `@RequestBody` でリクエストボディを取得できる。

### レスポンスのステータスコードを指定する
```java:
package sample.springboot.web;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/hello")
public class HelloController {
    
    @RequestMapping(method=RequestMethod.GET)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public void getMethod() {
    }
}
```

```bash:動作確認
$ curl http://localhost:8080/hello -v
（略）

< HTTP/1.1 400 Bad Request
* Server Apache-Coyote/1.1 is not blacklisted
< Server: Apache-Coyote/1.1
< Content-Length: 0
< Date: Wed, 29 Apr 2015 11:50:08 GMT
< Connection: close

（略）
```

- メソッドを `@ResponseStatus` でアノテートし、 `value` にステータスコードを指定すると、そのレスポンスのステータスコードを指定できる。
- 何も指定しない場合は `200 OK` が返される。

### レスポンスの返し方色々
[Java - Spring MVCのコントローラでの戻り値いろいろ - Qiita](http://qiita.com/tag1216/items/3680b92cf96eb5a170f0)

@tag1216 さんの上記ページに、分かりやすくまとめられていました。

## 例外ハンドリング
デフォルトだと、以下のように例外がハンドリングされる。

- REST のクライアントの場合
    - スローされた例外の情報や、 HTTP のステータスコードを保持した JSON 文字列。

```json:
{"timestamp":1430484452755,"status":500,"error":"Internal Server Error","exception":"sample.springboot.web.MyException","message":"No message available","path":"/hello"}
```

- ブラウザの場合
    - デフォルトのエラーページ（Whilelabel Error Page）

![spring-boot.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/db3749ce-4853-3ec2-b7d8-14b4f96c1d40.jpeg)

### 特定の例外がスローされたときのステータスコードを指定する
```java:MyException.java
package sample.springboot.web;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.BAD_REQUEST)
public class MyException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    
    public MyException(String msg) {
        super(msg);
    }
}
```

```java:WebApiController.java
package sample.springboot.web;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class WebApiController {

    @RequestMapping(method=RequestMethod.GET)
    public void method1() {
        throw new MyException("test exception");
    }
}
```

```bash:動作確認
$ curl http://localhost:8080/api
{"timestamp":1430489386562,"status":400,"error":"Bad Request","exception":"sample.springboot.web.MyException","message":"test exception","path":"/api"}
```

- 自作の例外クラスを `@ResponseStatus` でアノテートすることで、その例外がスローされたときのステータスコードを指定できる。
- ブラウザからアクセスした場合は、デフォルトのエラーページが表示される。

![spring-boot.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/835c6cb1-4e67-cf32-c149-cf8937931989.jpeg)

### ほぼ全ての例外をハンドリングする
```java:MyExceptionResolver.java
package sample.springboot.web;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

@Component
public class MyExceptionResolver implements HandlerExceptionResolver {
    
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        System.out.println(ex.getClass() + " : " + ex.getMessage());
        
        ModelAndView mv = new ModelAndView();
        mv.setViewName("my-error");
        
        return mv;
    }
}
```

```html:src/main/resources/templates/my-error.html
<h1>My Error Page</h1>
```

```java:WebApiController.java
package sample.springboot.web;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class WebApiController {

    @RequestMapping(method=RequestMethod.GET)
    public void method1() {
        throw new MyException("test exception");
    }
    
    @RequestMapping(value="/null", method=RequestMethod.GET)
    public void method2() {
        throw new NullPointerException("test exception");
    }
}
```

```bash:動作確認
$ curl http://localhost:8080/api
{"timestamp":1430490625809,"status":400,"error":"Bad Request","exception":"sample.springboot.web.MyException","message":"test exception","path":"/api"}

$ curl http://localhost:8080/api/null
<h1>My Error Page</h1>
```

```text:サーバーコンソール出力
class java.lang.NullPointerException : test exception
```

- `HandlerExceptionResolver` を実装したクラスを作成し、 `@Component` でコンテナに登録する。
- すると、コントローラで例外が発生すると登録したクラスの `resolveException()` メソッドが呼ばれるようになる。
    - ただし、 `@ResponseStatus` でアノテートされたクラスがスローされた場合は呼ばれない。
- `resolveException()` メソッドは `ModelAndView` を返すようになっているので、任意のページを表示させることができる。

#### Web API のアクセスの場合は json で返したい
```java:MyExceptionResolver.java
package sample.springboot.web;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

@Component
public class MyExceptionResolver implements HandlerExceptionResolver {
    
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        if (isRestController(handler)) {
            return null;
        }
        
        ModelAndView mv = new ModelAndView();
        mv.setViewName("my-error");
        
        return mv;
    }
    
    private boolean isRestController(Object handler) {
        if (handler instanceof HandlerMethod) {
            HandlerMethod method = (HandlerMethod)handler;
            
            return method.getMethod().getDeclaringClass().isAnnotationPresent(RestController.class);
        }
        
        return false;
    }
}
```

```bash:動作確認
$ curl http://localhost:8080/api
{"timestamp":1430490748092,"status":400,"error":"Bad Request","exception":"sample.springboot.web.MyException","message":"test exception","path":"/api"}

$ curl http://localhost:8080/api/null
{"timestamp":1430490749586,"status":500,"error":"Internal Server Error","exception":"java.lang.NullPointerException","message":"test exception","path":"/api/null"}
```

- コントローラが `@RestController` でアノテートされている場合は、 `resolveException()` で null を返すようにする。
- すると、レスポンスがデフォルトのハンドリング方法で処理されるようになる（クライアントが curl のような非ブラウザなら json になる）。
    - ブラウザでアクセスした場合は、デフォルトのエラーページ（Whitelabel Error Page）が表示される。
    - ブラウザから画面遷移でアクセスする場合は、 `@Controller` でアノテートされたコントローラクラスにアクセスするようにし、 `resolveException()` で適切なエラーページに飛ばしてあげるようにする。

### コントローラ単位で例外ハンドリングを定義する
```java:WebApiController.java
package sample.springboot.web;

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.RestController;

@RestController
@RequestMapping("/api")
public class WebApiController {

    @RequestMapping(method=RequestMethod.GET)
    public void method1() {
        throw new MyException("test exception");
    }
    
    @RequestMapping(value="/null", method=RequestMethod.GET)
    public void method2() {
        throw new NullPointerException("test exception");
    }
    
    @ExceptionHandler(NullPointerException.class)
    public String handling(NullPointerException e) {
        return "{\"message\":\"" + e.getMessage() + "\"}";
    }
}
```

```bash:動作確認
$ curl http://localhost:8080/api/null
{"message":"test exception"}
```

- `@ExceptionHandler` でアノテートしたメソッドを定義すると、そのコントローラ内でだけ有効な例外ハンドリングができる。
- `@ExceptionHandler` の `value` には、ハンドリングしたい例外の `Class` オブジェクトを渡す。




## 静的ファイルを配置する
```text:フォルダ構成
|-build.gradle
`-src/main/resources/
  |-static/
  | `-static.html
  |-public/
  | `-public.html
  |-resources/
  | `-resources.html
  `-META-INF/resources/
    `-m-resourceshtml
```

各 HTML ファイルの中身は、ファイル名が書かれただけのプレーンテキスト。

この状態で Spring Boot を起動して、以下のようにアクセスする。

```bash:
$ curl http://localhost:8080/static.html
static.html

$ curl http://localhost:8080/public.html
public.html

$ curl http://localhost:8080/resources.html
resources.html

$ curl http://localhost:8080/m-resources.html
m-resources.html
```

- クラスパス以下の、次のフォルダにファイルを配置すると、静的ファイルとしてアクセスできる。
    - `static`
    - `public`
    - `resources`
    - `META-INF/resources`

## WebJars を利用する
### WebJars とは
jQuery とか Bootstrap のようなクライアントサイドのライブラリを jar に固めて、 Java のライブラリと同じ要領で Maven や Gradle で依存管理できるようにしたサービス。

[WebJars - Web Libraries in Jars](http://www.webjars.org/)

### jQuery UI を入れてみる
[ここ][2] で、利用できるライブラリを調べられる。

```diff:build.gradle
dependencies {
    compile 'org.springframework.boot:spring-boot-starter-web'
+   compile 'org.webjars:jquery-ui:1.11.4'
}
```

```html:src/main/resources/static/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>jQuery UI by WebJars</title>
    
    <link rel="stylesheet" href="/webjars/jquery-ui/1.11.4/jquery-ui.min.css" />
    
    <script src="/webjars/jquery/1.11.1/jquery.min.js"></script>
    <script src="/webjars/jquery-ui/1.11.4/jquery-ui.min.js"></script>
    <script>
    $(function() {
        $('button')
          .button()
          .on('click', function() {
              alert('Hello WebJars!!');
          });
    });
    </script>
  </head>
  <body>
    <button>Hello</button>
  </body>
</html>
```

サーバーを起動して、ブラウザで `http://localhost:8080/` にアクセスする。

![spring-boot.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/bfb69a74-61cc-d3be-eda4-4214c5e385db.jpeg)

![spring-boot.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/8f2c0b2e-6f08-7900-c1e8-43888ce137cc.jpeg)

- WebJars で追加したライブラリは、 `webjars/` 以下のパスからアクセスできる。
- フォルダ構成は、 jar の中を見るか[前述のページ][2]の右端にある `Files` のリンクをクリックすれば分かる。

## テンプレートエンジンを利用する
Spring Boot 的には JSP はおすすめしないらしい。

[はじめてのSpring Boot][3] でも紹介されてる Thymeleaf を使ってみる。

### Hello World
#### 実装
```diff:build.gradle
dependencies {
    compile 'org.springframework.boot:spring-boot-starter-web'
+   compile 'org.springframework.boot:spring-boot-starter-thymeleaf'
    compile 'org.webjars:jquery-ui:1.11.4'
}
```

```java:HelloController.java
package sample.springboot.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping("/hello")
public class HelloController {
    
    @RequestMapping(method=RequestMethod.GET)
    public String hello() {
        return "hello";
    }
}
```

```html:src/main/resources/templates/hello.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello Thymeleaf</title>
  </head>
  <body>
    <h1>Hello Thymeleaf</h1>
  </body>
</html>
```

#### 動作確認
ブラウザで `http://localhost:8080/hello` にアクセスする。

![spring-boot.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/095d6292-e9ad-8539-8739-ccfe5bf9bd1d.jpeg)

#### 説明
##### 依存関係の追加
```diff:build.gradle
dependencies {
    compile 'org.springframework.boot:spring-boot-starter-web'
+   compile 'org.springframework.boot:spring-boot-starter-thymeleaf'
    compile 'org.webjars:jquery-ui:1.11.4'
}
```

- Thymeleaf を使えるように依存関係を追加する。

##### コントローラの実装
```java:
@Controller
@RequestMapping("/hello")
public class HelloController {
    
    @RequestMapping(method=RequestMethod.GET)
    public String hello() {
        return "hello";
    }
}
```

- テンプレートを返す場合は、 `@RestController` ではなく `@Controller` でクラスをアノテートする。
- メソッドの戻り値に、表示するテンプレートのパスを指定する。
    - テンプレートファイルは、クラスパス上の `templates` パッケージの下に配置する。
    - コントローラのメソッドが返した文字列は、この `templates` パッケージからの相対パスになる（拡張子は省略可）。

### 画面に値を埋め込む
```java:Hoge.java
package sample.springboot.web;

public class Hoge {
    
    public int id;
    public String value;
    
    @Override
    public String toString() {
        return "Hoge [id=" + id + ", value=" + value + "]";
    }
}
```

```java:HelloController.java
package sample.springboot.web;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping("/hello")
public class HelloController {
    
    @RequestMapping(method=RequestMethod.GET)
    public String hello(Model model) {
        Hoge hoge = new Hoge();
        hoge.id = 10;
        hoge.value = "hoge";
        
        model.addAttribute("myData", hoge);
        
        return "hello";
    }
}
```

```html:hello.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
  <head>
    <meta charset="UTF-8" />
    <title>Hello Thymeleaf</title>
  </head>
  <body>
    <dl>
      <dt>id</dt>
      <dd th:text="${myData.id}"></dd>
      
      <dt>value</dt>
      <dd th:text="${myData.value}"></dd>
    </dl>
  </body>
</html>
```

ブラウザで `http://localhost:8080/hello` にアクセスする。

![spring-boot.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/0e3df13f-6533-7ae3-714f-399675cd37d8.jpeg)

- コントローラのメソッドで `Model` を引数に受け取るようにする。
- この `Model` の `addAttribute()` メソッドを使って、画面で出力したい情報を設定する。
- 画面側では、まず Thymeleaf 用の名前空間を定義する（`xmlns:th`）
    - `th:text` 属性で、指定した値をテキストとして出力する。
    - `th:text` の値には、 `${...}` のように EL 式っぽく出力する値を指定する。

### 繰り返し出力
```java:HelloController.java
package sample.springboot.web;

import java.util.Arrays;
import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping("/hello")
public class HelloController {
    
    @RequestMapping(method=RequestMethod.GET)
    public String hello(Model model) {
        List<Hoge> list = Arrays.asList(
                            new Hoge() {{
                                id = 10;
                                value = "hoge";
                            }},
                            new Hoge() {{
                                id = 20;
                                value = "fuga";
                            }},
                            new Hoge() {{
                                id = 30;
                                value = "piyo";
                            }});
        
        model.addAttribute("hogeList", list);
        
        return "hello";
    }
}
```

```html:hello.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
  <head>
    <meta charset="UTF-8" />
    <title>Hello Thymeleaf</title>
  </head>
  <body>
    <dl th:each="hoge : ${hogeList}">
      <dt>id</dt>
      <dd th:text="${hoge.id}"></dd>
      
      <dt>value</dt>
      <dd th:text="${hoge.value}"></dd>
    </dl>
  </body>
</html>
```

![spring-boot.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/c6b30317-6e42-b011-c2fa-8401b4f442a4.jpeg)

- `th:each` で、指定したコレクションを繰り返し処理できる。

### その他の使い方
ここに書いてたらキリがないので、 [公式ドキュメント](http://www.thymeleaf.org/documentation.html) を参照。

気が向いたら別途まとめる。

## ホットデプロイ
```diff:build.gradle
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.2.3.RELEASE'
+       classpath 'org.springframework:springloaded:1.2.1.RELEASE'
    }
}

apply plugin: 'java'
apply plugin: 'spring-boot'

sourceCompatibility = '1.8'
targetCompatibility = '1.8'

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.springframework.boot:spring-boot-starter-web'
}

jar.baseName = 'spring-boot-sample'
```

buildscript の依存関係に `org.springframework:springloaded:1.2.1.RELEASE` を追加する。
あとは、普通に `gradle bootRun` すれば、ホットデプロイが有効になる。

テンプレートエンジンに Thymeleaf を利用している場合は、キャッシュ機能をオフにしておく必要がある。

```properties:application.properties
spring.thymeleaf.cache=false
```

Thymeleaf 以外のテンプレートエンジンを利用している場合は、 [このページ](http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/html/howto-hotswapping.html) を参照。

### IntelliJ IDEA を使用している場合
`build.gradle` に以下を追記する必要がある。

```groovy:build.gradle
apply plugin: 'idea'

idea {
    module {
        inheritOutputDirs = false
        outputDir = file("$buildDir/classes/main/")
    }
}
```

デフォルトだと、 IlleliJ がコンパイル結果を出力する先が Gradle の出力先と異なるためファイルの監視がうまくいかないらしく、そのへんを変更しているらしい。

参考：[80. Hot swapping](http://docs.spring.io/spring-boot/docs/current/reference/html/howto-hotswapping.html#howto-reload-springloaded-gradle-and-intellij-idea)

# データベースアクセス
## Hello World
```groovy:build.gradle
dependencies {
    compile 'org.hsqldb:hsqldb'
    compile 'org.springframework.boot:spring-boot-starter-jdbc'
}
```

```java:Main.java
package sample.springboot;

import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.method();
        }
    }
    
    @Autowired
    private JdbcTemplate jdbc;
    
    public void method() {
        this.jdbc.execute("CREATE TABLE TEST_TABLE (ID INTEGER NOT NULL IDENTITY, VALUE VARCHAR(256))");
        
        this.jdbc.update("INSERT INTO TEST_TABLE (VALUE) VALUES (?)", "hoge");
        this.jdbc.update("INSERT INTO TEST_TABLE (VALUE) VALUES (?)", "fuga");
        this.jdbc.update("INSERT INTO TEST_TABLE (VALUE) VALUES (?)", "piyo");
        
        List<Map<String, Object>> list = this.jdbc.queryForList("SELECT * FROM TEST_TABLE");
        list.forEach(System.out::println);
    }
}
```

```text:動作確認
{ID=0, VALUE=hoge}
{ID=1, VALUE=fuga}
{ID=2, VALUE=piyo}
```

- 依存関係に `spring-boot-starter-jdbc` と、使用する DB （`org.hsqldb:hsqldb`）を追加する。
- すると、指定した DB をオンメモリで利用できるようになる。
- オンメモリなので、 JVM が停止するとデータは失われる。
- HSQLDB の他に H2 と Derby を同じく組み込みで利用できる。

## データをファイルに永続化する
```properties:application.properties
spring.datasource.url=jdbc:hsqldb:file:./db/testdb;shutdown=true
```

- [プロパティファイル](#外部設定externalized-configurationを利用する)で `spring.datasource.url` を定義することで、 JDBC 接続するときの URL を指定できる。
- HSQLDB の場合は、 URL でデータをファイルに保存するかどうかを指定できるので、上記のように設定すればデータをファイルに永続化できる。

## 外部のデータベースを利用する
ローカルの MySQL を利用する。

**MySQL のテーブル**

![spring-boot.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/418b89c9-b18f-116c-c642-6cec6d26871b.jpeg)

**実装**

```groovy:build.gradle
dependencies {
    compile 'org.springframework.boot:spring-boot-starter-jdbc'
    compile 'mysql:mysql-connector-java:5.1.35'
}
```

```properties:application.properties
spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=test
spring.datasource.password=test
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
```

```java:Main.java
package sample.springboot;

import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.method();
        }
    }
    
    @Autowired
    private JdbcTemplate jdbc;
    
    public void method() {
        List<Map<String, Object>> list = this.jdbc.queryForList("SELECT * FROM TEST_TABLE");
        list.forEach(System.out::println);
    }
}
```

```test:実行結果
{id=1, value=hoge}
{id=2, value=fuga}
{id=3, value=piyo}
```

- `application.properties` に接続設定を記述することで、外部の DB に接続できる。

## JPA を利用する
### 基本
```diff:build.gradle
dependencies {
-   compile 'org.springframework.boot:spring-boot-starter-jdbc'
+   compile 'org.springframework.boot:spring-boot-starter-data-jpa'
    compile 'org.hsqldb:hsqldb'
}
```

```properties:application.properties
spring.datasource.url=jdbc:hsqldb:file:./db/testdb;shutdown=true
spring.jpa.hibernate.ddl-auto=update
```

```java:MyEntity.java
package sample.springboot.jpa;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class MyEntity {
    
    @Id @GeneratedValue
    private Long id;
    private String value;

    public MyEntity(String value) {
        this.value = value;
    }
    
    private MyEntity() {}

    @Override
    public String toString() {
        return "MyEntity [id=" + id + ", value=" + value + "]";
    }
}
```

```java:MyEntityRepository.java
package sample.springboot.jpa;

import org.springframework.data.jpa.repository.JpaRepository;

public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
}
```

```java:Main.java
package sample.springboot;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import sample.springboot.jpa.MyEntity;
import sample.springboot.jpa.MyEntityRepository;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.method();
        }
    }
    
    @Autowired
    private MyEntityRepository repository;
    
    public void method() {
        this.repository.save(new MyEntity("test"));
        
        this.repository.findAll().forEach(System.out::println);
    }
}
```

```bash:動作確認
$ gradle bootRun
MyEntity [id=1, value=test]

$ gradle bootRun
MyEntity [id=1, value=test]
MyEntity [id=2, value=test]

$ gradle bootRun
MyEntity [id=1, value=test]
MyEntity [id=2, value=test]
MyEntity [id=3, value=test]
```

- JPA を使う場合は、 `org.springframework.boot:spring-boot-starter-data-jpa` を依存関係に追加する。
- JPA の実装には Hibernate が利用される。
    - デフォルトだとテーブルが毎回作りなおされるので、 `spring.jpa.hibernate.ddl-auto=update` を設定している。
- `JpaRepository` を継承したインターフェースを定義すると、 Spring が良しなにデータアクセスの実装を作ってくれる。

### メソッド名からのクエリ自動生成

**データベース**

![spring-boot.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/345dea68-58a4-a9cb-053d-e2dda303be3b.jpeg)

**エンティティ**

```java:Hoge.java
package sample.springboot.jpa;

import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Hoge {
    
    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;
    private int number;
    private String string;
    @Embedded
    private Fuga fuga;
    
    @Override
    public String toString() {
        return "Hoge [id=" + id + ", number=" + number + ", string=" + string + ", fuga=" + fuga + "]";
    }
}
```

```java:Fuga.java
package sample.springboot.jpa;

import javax.persistence.Embeddable;

@Embeddable
public class Fuga {
    
    private String value;

    @Override
    public String toString() {
        return "Fuga [value=" + value + "]";
    }
}
```

**リポジトリインターフェース**

```java:HogeRepository.java
package sample.springboot.jpa;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;

public interface HogeRepository extends JpaRepository<Hoge, Long> {
    
    List<Hoge> findByNumber(int number);
    
    List<Hoge> findByNumberOrderByIdDesc(int number);
    
    List<Hoge> findByStringLike(String string);
    
    List<Hoge> findByNumberLessThan(int number);

    List<Hoge> findByStringIgnoreCase(String string);

    List<Hoge> findByFugaValue(String string);

    long countByStringLike(String string);

    List<Hoge> findByNumberAndStringLike(int number, String string);

    List<Hoge> findByNumberOrString(int number, String string);
}
```

**動作確認**

```java:Main.java
package sample.springboot;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import sample.springboot.jpa.Hoge;
import sample.springboot.jpa.HogeRepository;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.method();
        }
    }
    
    @Autowired
    private HogeRepository repository;
    
    public void method() {
        print("findByNumber",              repository.findByNumber(1));
        print("findByNumberAndStringLike", repository.findByNumberAndStringLike(1, "%e"));
        print("findByNumberOrString",      repository.findByNumberOrString(2, "seven"));
        print("findByNumberOrderByIdDesc", repository.findByNumberOrderByIdDesc(2));
        print("findByStringLike",          repository.findByStringLike("t%"));
        print("findByNumberLessThan",      repository.findByNumberLessThan(3));
        print("findByStringIgnoreCase",    repository.findByStringIgnoreCase("FIVE"));
        print("findByFugaValue",           repository.findByFugaValue("hoge"));
        print("countByStringLike",         repository.countByStringLike("%o%"));
    }
    
    private void print(String methodName, List<Hoge> list) {
        System.out.println("<<" + methodName + ">>");
        list.forEach(System.out::println);
        System.out.println();
    }
    
    private void print(String methodName, long number) {
        System.out.println("<<" + methodName + ">>");
        System.out.println(number);
        System.out.println();
    }
}
```

```text:実行結果
<<findByNumber>>
Hoge [id=1, number=1, string=one, fuga=Fuga [value=hoge]]
Hoge [id=2, number=1, string=two, fuga=Fuga [value=fuga]]
Hoge [id=3, number=1, string=three, fuga=Fuga [value=piyo]]

<<findByNumberOrderByIdDesc>>
Hoge [id=5, number=2, string=five, fuga=Fuga [value=fuga]]
Hoge [id=4, number=2, string=four, fuga=Fuga [value=hoge]]

<<findByStringLike>>
Hoge [id=2, number=1, string=two, fuga=Fuga [value=fuga]]
Hoge [id=3, number=1, string=three, fuga=Fuga [value=piyo]]

<<findByNumberLessThan>>
Hoge [id=1, number=1, string=one, fuga=Fuga [value=hoge]]
Hoge [id=2, number=1, string=two, fuga=Fuga [value=fuga]]
Hoge [id=3, number=1, string=three, fuga=Fuga [value=piyo]]
Hoge [id=4, number=2, string=four, fuga=Fuga [value=hoge]]
Hoge [id=5, number=2, string=five, fuga=Fuga [value=fuga]]

<<findByStringIgnoreCase>>
Hoge [id=5, number=2, string=five, fuga=Fuga [value=fuga]]

<<findByFugaValue>>
Hoge [id=1, number=1, string=one, fuga=Fuga [value=hoge]]
Hoge [id=4, number=2, string=four, fuga=Fuga [value=hoge]]
Hoge [id=7, number=3, string=seven, fuga=Fuga [value=hoge]]

<<countByStringLike>>
3

<<findByNumberAndStringLike>>
Hoge [id=1, number=1, string=one, fuga=Fuga [value=hoge]]
Hoge [id=3, number=1, string=three, fuga=Fuga [value=piyo]]

<<findByNumberOrString>>
Hoge [id=4, number=2, string=four, fuga=Fuga [value=hoge]]
Hoge [id=5, number=2, string=five, fuga=Fuga [value=fuga]]
Hoge [id=7, number=3, string=seven, fuga=Fuga [value=hoge]]
```

![spring-boot.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/345dea68-58a4-a9cb-053d-e2dda303be3b.jpeg)

- `Repository` を継承したインターフェースに `find~~` のようなメソッドを定義すると、 Spring が良しなに解釈してクエリを自動生成してくれる。
- 基本は、 `findBy<条件とするプロパティの名前>` で定義する。
- `And` や `Or` で連結できる。
- `OrderBy<プロパティ名><Asc | Desc>` で、ソートを指定できる。
- `Like` をつければ文字列のあいまい検索ができる。
- `LessThan`, `GreaterThan`, `Between` なども使える。
- `IgnoreCase` をつければ、大文字小文字の区別なしで比較できる。
- `count~~` とすると、検索結果のエンティティ数を取得できる。
- 組み込み可能クラスのプロパティを条件にする場合は、 `findBy<組み可能クラス><組み込み可能クラスのプロパティ>` と繋げる。

### JPQL を使用する
```java:HogeRepository.java
package sample.springboot.jpa;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

public interface HogeRepository extends JpaRepository<Hoge, Long> {
    
    @Query("SELECT h FROM Hoge h WHERE (h.id % 2) = 0")
    List<Hoge> findEvenIdEntities();
}
```

```java:Main.java
package sample.springboot;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import sample.springboot.jpa.HogeRepository;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.method();
        }
    }
    
    @Autowired
    private HogeRepository repository;
    
    public void method() {
        this.repository.findEvenIdEntities().forEach(System.out::println);
    }
}
```

```text:実行結果
Hoge [id=2, number=1, string=two, fuga=Fuga [value=fuga]]
Hoge [id=4, number=2, string=four, fuga=Fuga [value=hoge]]
Hoge [id=6, number=3, string=six, fuga=Fuga [value=piyo]]
```

![spring-boot.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/345dea68-58a4-a9cb-053d-e2dda303be3b.jpeg)

- `@Query` でメソッドをアノテートすることで、 JPQL を指定することができる。
- JPQL は、 `@Query` の `value` に設定する。

### EntityManager を取得する
```java:Main.java
package sample.springboot;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import sample.springboot.jpa.Hoge;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.method();
        }
    }
    
    @Autowired
    private EntityManager em;
    
    public void method() {
        TypedQuery<Hoge> query = this.em.createQuery("SELECT h FROM Hoge h WHERE h.id=:id", Hoge.class);
        query.setParameter("id", 3L);
        
        Hoge hoge = query.getSingleResult();
        
        System.out.println(hoge);
    }
}
```

```text:実行結果
Hoge [id=3, number=1, string=three, fuga=Fuga [value=piyo]]
```

- `@Autowired` を使って普通に `EntityManager` をインジェクションできる。

## 宣言的トランザクションを使用する
```java:MyService.java
package sample.springboot.jpa;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
public class MyService {
    
    @Autowired
    private HogeRepository repository;
    
    public void save(String value) {
        Hoge hoge = new Hoge(value);
        this.repository.save(hoge);
    }
    
    public void saveAndThrowRuntimeException(String value) {
        this.save(value);
        throw new RuntimeException("test");
    }

    @Transactional
    public void saveAndThrowRuntimeExceptionWithTransactional(String value) {
        this.saveAndThrowRuntimeException(value);
    }
    
    @Transactional
    public void saveAndThrowExceptionWithTransactional(String value) throws Exception {
        this.save(value);
        throw new Exception("test");
    }
    
    public void show() {
        this.repository.findAll().forEach(System.out::println);
    }
}
```

```java:Main.java
package sample.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import sample.springboot.jpa.MyService;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) throws Exception {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            MyService s = ctx.getBean(MyService.class);
            
            s.save("normal");
            
            try {
                s.saveAndThrowRuntimeException("runtime exception without @Transactional");
            } catch (Exception e) {}
            
            try {
                s.saveAndThrowRuntimeExceptionWithTransactional("runtime exception with @Transactional");
            } catch (Exception e) {}
            
            try {
                s.saveAndThrowExceptionWithTransactional("exception with @Transactional");
            } catch (Exception e) {}
            
            s.show();
        }
    }
}
```

```text:実行結果
Hoge [id=1, value=normal]
Hoge [id=2, value=runtime exception without @Transactional]
Hoge [id=4, value=exception with @Transactional]
```

- `@Transactional` でメソッドをアノテートすると、そのメソッドの前後がトランザクション境界になる。
- トランザクション境界内で `RuntimeException` およびそのサブクラスがスローされると、トランザクションはロールバックされる。
- `@Transactional` でアノテートされていなかったり、 `Exception` およびそのサブクラスがスローされた場合は、ロールバックされない。
- `Exception` がスローされた場合もロールバックして欲しい場合は、 `@Transactional(rollbackFor=Exception.class)` のように設定する。

## Flyway でマイグレーションする
```diff:build.gradle
dependencies {
    compile 'org.springframework.boot:spring-boot-starter-data-jpa'
    compile 'org.hsqldb:hsqldb'
+   compile 'org.flywaydb:flyway-core'
}
```

```properties:application.properties
spring.jpa.hibernate.ddl-auto=none
```

```sql:src/main/resources/db/migration/V1__create_database.sql
CREATE TABLE HOGE (
    ID INTEGER NOT NULL IDENTITY,
    VALUE VARCHAR(256)
);

INSERT INTO HOGE (VALUE) VALUES ('HOGE');
INSERT INTO HOGE (VALUE) VALUES ('FUGA');
INSERT INTO HOGE (VALUE) VALUES ('PIYO');
```

```java:Main.java
package sample.springboot;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import sample.springboot.jpa.HogeRepository;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) throws Exception {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.method();
        }
    }
    
    @Autowired
    private HogeRepository repository;
    
    public void method() {
        this.repository.findAll().forEach(System.out::println);
    }
}
```

```text:実行結果
Hoge [id=0, value=HOGE]
Hoge [id=1, value=FUGA]
Hoge [id=2, value=PIYO]
```

- Flyway を依存関係に追加するだけで、サーバー起動時にマイグレーションを実行してくれるようになる。
- JPA を使う場合は、 JPA が DB を自動生成しないようにしないといけないので、 `spring.jpa.hibernate.ddl-auto=none` を指定する。
- Flyway 自体の使い方については [こちら](http://qiita.com/opengl-8080/items/6368c19a06521b65a655) を参照。

## 複数のデータソースを使用する
### 基本

**実装**

```java:PrimaryDataSourceConfiguration.java
package sample.springboot;

import javax.sql.DataSource;

import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;

@Configuration
public class PrimaryDataSourceConfiguration {
    
    @Bean @Primary
    public DataSource createPrimaryDataSource() {
        return DataSourceBuilder
            .create()
            .driverClassName("org.hsqldb.jdbcDriver")
            .url("jdbc:hsqldb:mem:primary")
            .username("SA")
            .password("")
            .build();
    }
    
    @Bean @Primary
    public JdbcTemplate createPrimaryJdbcTemplate(DataSource ds) {
        return new JdbcTemplate(ds);
    }
}
```

```java:SecondaryDataSourceConfiguration.java
package sample.springboot;

import javax.sql.DataSource;

import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

@Configuration
public class SecondaryDataSourceConfiguration {
    
    @Bean @MySecondary
    public DataSource createSecondaryDataSource() {
        return DataSourceBuilder
                .create()
                .driverClassName("org.hsqldb.jdbcDriver")
                .url("jdbc:hsqldb:mem:secondary")
                .username("SA")
                .password("")
                .build();
    }
    
    @Bean @MySecondary
    public JdbcTemplate createSecondaryJdbcTemplate(@MySecondary DataSource ds) {
        return new JdbcTemplate(ds);
    }
}
```

```java:MySecondary.java
package sample.springboot;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.beans.factory.annotation.Qualifier;

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
public @interface MySecondary {
}
```

```java:MyDatabaseAccess.java
package sample.springboot;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

@Component
public class MyDatabaseAccess {

    private static final String CREATE_TABLE_SQL = "CREATE TABLE TEST_TABLE (VALUE VARCHAR(256))";
    private static final String INSERT_SQL = "INSERT INTO TEST_TABLE VALUES (?)";
    private static final String SELECT_SQL = "SELECT * FROM TEST_TABLE";
    
    @Autowired
    private JdbcTemplate primary;
    
    @Autowired @MySecondary
    private JdbcTemplate secondary;
    
    public void initialize() {
        this.primary.execute(CREATE_TABLE_SQL);
        this.secondary.execute(CREATE_TABLE_SQL);
    }
    
    public void insertPrimary(String value) {
        this.primary.update(INSERT_SQL, value);
    }
    
    public void insertSecondary(String value) {
        this.secondary.update(INSERT_SQL, value);
    }
    
    public void showRecords() {
        System.out.println("Primary >>>>");
        this.primary.queryForList(SELECT_SQL).forEach(System.out::println);
        
        System.out.println("Secondary >>>>");
        this.secondary.queryForList(SELECT_SQL).forEach(System.out::println);
    }
}
```

```java:Main.java
package sample.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            MyDatabaseAccess db = ctx.getBean(MyDatabaseAccess.class);
            
            db.initialize();
            
            db.insertPrimary("primary!!");
            db.insertSecondary("secondary!!");
            
            db.showRecords();
        }
    }
}
```

**動作確認**

```text:コンソール出力
Primary >>>>
{VALUE=primary!!}

Secondary >>>>
{VALUE=secondary!!}
```

**説明**

```java:PrimaryDataSourceConfiguration.java
    @Bean @Primary
    public DataSource createPrimaryDataSource() {
        return DataSourceBuilder
            .create()
            .driverClassName("org.hsqldb.jdbcDriver")
            .url("jdbc:hsqldb:mem:primary")
            .username("SA")
            .password("")
            .build();
    }
    
    @Bean @Primary
    public JdbcTemplate createPrimaryJdbcTemplate(DataSource ds) {
        return new JdbcTemplate(ds);
    }
```

- `@Bean` を使って、 `DataSource` のビーンを定義している（`createPrimaryDataSource()`）。
- 作成した `DataSource` を引数に受け取りつつ、さらに `JdbcTemplate` のビーンを定義している（`createPrimaryJdbcTemplate()`）。
- `DataSource` を複数定義するときは、一方の定義を `@Primary` でアノテートする。
    - `@Primary` は、デフォルトでインジェクションされるビーンを指定するためのアノテーション。
    - ビーンの候補が複数存在する状態で限定子を指定しないと、 `@Primary` でアノテートされたビーンがインジェクションされる。
- `DataSource` のインスタンスは、 `DataSourceBuilder` を使って作成できる。

```java:SecondaryDataSourceConfiguration.java
    @Bean @MySecondary
    public DataSource createSecondaryDataSource() {
        return DataSourceBuilder
                .create()
                .driverClassName("org.hsqldb.jdbcDriver")
                .url("jdbc:hsqldb:mem:secondary")
                .username("SA")
                .password("")
                .build();
    }
    
    @Bean @MySecondary
    public JdbcTemplate createSecondaryJdbcTemplate(@MySecondary DataSource ds) {
        return new JdbcTemplate(ds);
    }
```

- ２つ目の `DataSource` の定義には、自作の限定子を付与している。
    - 限定子については [こちら](#限定子を自作する) を参照。

```java:MyDatabaseAccess.java
    @Autowired
    private JdbcTemplate primary;
    
    @Autowired @MySecondary
    private JdbcTemplate secondary;
```

- インジェクションするときに、 `@Autowired` だけなら `@Primary` でアノテートした方のビーンが、  
自作限定子でアノテートすれば、対応するビーンがインジェクションされる。
- あとは、だいたい今まで通りにデータベースアクセスができる。

### 宣言的トランザクション
複数の `DataSource` を定義した場合、そのままだと `@Primary` じゃない方のデータソースについて、宣言的トランザクションが使用できない。

`@Primary` でない方のデータソースでも宣言的トランザクションを使用する場合は、以下のように実装する。

**実装**

```diff:PrimaryDataSourceConfiguration.java
package sample.springboot;

import javax.sql.DataSource;

import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;
+ import org.springframework.jdbc.datasource.DataSourceTransactionManager;
+ import org.springframework.transaction.PlatformTransactionManager;

@Configuration
public class PrimaryDataSourceConfiguration {
    
    @Bean @Primary
    public DataSource createPrimaryDataSource() {
        return DataSourceBuilder
            .create()
            .driverClassName("org.hsqldb.jdbcDriver")
            .url("jdbc:hsqldb:mem:primary")
            .username("SA")
            .password("")
            .build();
    }
    
    @Bean @Primary
    public JdbcTemplate createPrimaryJdbcTemplate(DataSource ds) {
        return new JdbcTemplate(ds);
    }
    
+   @Bean @Primary
+   public PlatformTransactionManager createTransactionManager(DataSource ds) {
+       return new DataSourceTransactionManager(ds);
+   }
}
```

```diff:SecondaryDataSourceConfiguration.java
package sample.springboot;

import javax.sql.DataSource;

import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
+ import org.springframework.jdbc.datasource.DataSourceTransactionManager;
+ import org.springframework.transaction.PlatformTransactionManager;

@Configuration
public class SecondaryDataSourceConfiguration {
    
+   public static final String TRANSACTION_MANAGER_NAME = "secondary-tx-manager";
    
    @Bean @MySecondary
    public DataSource createSecondaryDataSource() {
        return DataSourceBuilder
                .create()
                .driverClassName("org.hsqldb.jdbcDriver")
                .url("jdbc:hsqldb:mem:secondary")
                .username("SA")
                .password("")
                .build();
    }
    
    @Bean @MySecondary
    public JdbcTemplate createSecondaryJdbcTemplate(@MySecondary DataSource ds) {
        return new JdbcTemplate(ds);
    }
    
+   @Bean(name=SecondaryDataSourceConfiguration.TRANSACTION_MANAGER_NAME)
+   public PlatformTransactionManager createTransactionManager(@MySecondary DataSource ds) {
+       return new DataSourceTransactionManager(ds);
+   }
}
```

```diff:MyDatabaseAccess.java
package sample.springboot;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
+ import org.springframework.transaction.annotation.Transactional;

@Component
public class MyDatabaseAccess {

    private static final String CREATE_TABLE_SQL = "CREATE TABLE TEST_TABLE (VALUE VARCHAR(256))";
    private static final String INSERT_SQL = "INSERT INTO TEST_TABLE VALUES (?)";
    private static final String SELECT_SQL = "SELECT * FROM TEST_TABLE";
    
    @Autowired
    private JdbcTemplate primary;
    
    @Autowired @MySecondary
    private JdbcTemplate secondary;
    
    public void initialize() {
        this.primary.execute(CREATE_TABLE_SQL);
        this.secondary.execute(CREATE_TABLE_SQL);
    }
    
-   public void insertPrimary(String value) {
-       this.primary.update(INSERT_SQL, value);
-   }
-   
-   public void insertSecondary(String value) {
-       this.secondary.update(INSERT_SQL, value);
-   }
    
+   @Transactional
+   public void insertPrimary(String value, boolean rollback) {
+       this.primary.update(INSERT_SQL, value);
+       if (rollback) throw new RuntimeException("test exception");
+   }
+   
+   @Transactional(SecondaryDataSourceConfiguration.TRANSACTION_MANAGER_NAME)
+   public void insertSecondary(String value, boolean rollback) {
+       this.secondary.update(INSERT_SQL, value);
+       if (rollback) throw new RuntimeException("test exception");
+   }
    
    public void showRecords() {
        System.out.println("Primary >>>>");
        this.primary.queryForList(SELECT_SQL).forEach(System.out::println);
        
        System.out.println("Secondary >>>>");
        this.secondary.queryForList(SELECT_SQL).forEach(System.out::println);
    }
    
}
```

```java:Main.java
package sample.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            MyDatabaseAccess db = ctx.getBean(MyDatabaseAccess.class);
            
            db.initialize();
            
            db.insertPrimary("primary commit!!", false);
            db.insertSecondary("secondary commit!!", false);
            
            try {
                db.insertPrimary("primary rollback!!", true);
            } catch (Exception e) {}
            
            try {
                db.insertSecondary("secondary rollback!!", true);
            } catch (Exception e) {}
            
            db.showRecords();
        }
    }
}
```

**動作確認**

```text:コンソール出力
Primary >>>>
{VALUE=primary commit!!}

Secondary >>>>
{VALUE=secondary commit!!}
```

**説明**

```java:PrimaryDataSourceConfiguration.java
    @Bean @Primary
    public PlatformTransactionManager createTransactionManager(DataSource ds) {
        return new DataSourceTransactionManager(ds);
    }
```

```java:SecondaryDataSourceConfiguration.java
    public static final String TRANSACTION_MANAGER_NAME = "secondary-tx-manager";

    ...

    @Bean(name=SecondaryDataSourceConfiguration.TRANSACTION_MANAGER_NAME)
    public PlatformTransactionManager createTransactionManager(@MySecondary DataSource ds) {
        return new DataSourceTransactionManager(ds);
    }
```

- 複数データソースを定義したうえで宣言的トランザクションを使用する場合は、 `PlatformTransactionManager` のビーンを定義する。
- `@Primary` の方は `@Primary` でアノテートするだけでいいが、そうでない方はビーン名を指定しておく。

```java:MyDatabaseAccess.java
    @Transactional
    public void insertPrimary(String value, boolean rollback) {
        this.primary.update(INSERT_SQL, value);
        if (rollback) throw new RuntimeException("test exception");
    }
    
    @Transactional(SecondaryDataSourceConfiguration.TRANSACTION_MANAGER_NAME)
    public void insertSecondary(String value, boolean rollback) {
        this.secondary.update(INSERT_SQL, value);
        if (rollback) throw new RuntimeException("test exception");
    }
```

- `@Primary` の `DataSource` を使用する場合は、従来通り `@Transactional` でアノテートすることで宣言的トランザクションが使用できる。
- `@Primary` でない方の `DataSource` を使用する場合は、 `@Transactional` の `value` に、 `PlatformTransactionManager` のビーン名を指定しなければならない。

**参考**

- [Transactions with JdbcTemplate | Java Creed](http://www.javacreed.com/transactions-with-jdbctemplate/)
- [Spring Boot Reference Guide](http://docs.spring.io/spring-boot/docs/1.2.0.BUILD-SNAPSHOT/reference/htmlsingle/#howto-two-datasources)
- [java - Spring Boot Multiple Datasource - Stack Overflow](http://stackoverflow.com/questions/27614301/spring-boot-multiple-datasource)

# 外部設定（Externalized Configuration）を利用する
## プロパティファイルを使用する
### 基本
```text:フォルダ構成
|-build.gradle
`-src/main/
  |-java/sample/springboot/
  |  `-Main.java
  `-resources/
    `-application.properties
```

```java:Main.java
package sample.springboot;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.hello();
        }
    }
    
    @Value("${sample.value}")
    private String value;
    
    public void hello() {
        System.out.println("sample.value = " + this.value);
    }
}
```

```text:実行結果
sample.value = Hello Properties File!!
```

- クラスパス直下に `application.properties` という名前でプロパティファイルを配置する。
- すると、 Spring Boot が自動でそのファイルを読み込んでくれる。
- プロパティファイルの値は、 `@Value` アノテーションを使うことで Bean にインジェクションできる。
    - `${プロパティ名}` という形式で取得したい値を指定する。

### ファイルの置き場所
プロパティファイルの置き場はいくつかあり、読み込みの優先順位が存在する。

1. 起動時に `--spring.config.location` で指定したファイル。
1. カレントディレクトリ直下の `config` ディレクトリにあるファイル。
1. カレントディレクトリにあるファイル。
1. クラスパス直下の `config` パッケージにあるファイル。
1. クラスパス直下にあるファイル。

数字が若い方が、優先順位が高い。
優先順位が下位の設定は、上位の設定で上書きされる。

```text:フォルダ構成（jar内）
|-application.properties
|-config/
|  `-application.properties
`-sample/springboot/
   `-Main.class
```

```text:フォルダ構成（実行時）
|-application.properties
|-other.properties
|-config/
| `-application.properties
`-build/libs/
  `-spring-boot-sample.jar
```

```properties:other.properties
value5=other
```

```properties:application.properties（カレントディレクトリのconfigディレクトリ直下）
value4=currentdir/config
value5=currentdir/config
```

```properties:application.properties（カレントディレクトリ直下）
value3=currentdir/
value4=currentdir/
value5=currentdir/
```

```properties:application.properties（クラスパス内のconfigパッケージ直下）
value2=classpath/config
value3=classpath/config
value4=classpath/config
value5=classpath/config
```

```properties:application.properties（クラスパス直下）
value1=classpath/
value2=classpath/
value3=classpath/
value4=classpath/
value5=classpath/
```

```java:Main.java
package sample.springboot;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.hello();
        }
    }
    
    @Value("${value1}") private String value1;
    @Value("${value2}") private String value2;
    @Value("${value3}") private String value3;
    @Value("${value4}") private String value4;
    @Value("${value5}") private String value5;
    
    public void hello() {
        System.out.println("value1=" + value1);
        System.out.println("value2=" + value2);
        System.out.println("value3=" + value3);
        System.out.println("value4=" + value4);
        System.out.println("value5=" + value5);
    }
}
```

```bash:実行
$ java -jar build/libs/spring-boot-sample.jar --spring.config.location=other.properties

value1=classpath/
value2=classpath/config
value3=currentdir/
value4=currentdir/config
value5=other
```

優先順位に合わせて設定が上書きされていっている。

### プロファイルを指定する
```text:フォルダ構成
|-application.properties
|-application-develop.properties
`-build/libs/
   `-spring-boot-sample.jar
```

```properties:application.properties
value=release module
```

```properties:application-develop.properties
value=develop module
```

```bash:実行
$ java -jar build/libs/spring-boot-sample.jar

value=release module

$ java -jar build/libs/spring-boot-sample.jar --spring.profiles.active=develop

value=develop module
```

- プロパティファイルを `application-<プロファイル名>.properties` という形式で作成する。
- コマンドライン引数などで、 `spring.profiles.active` に有効にしたいプロファイル名を指定する。
    - コマンドライン引数以外にも、システムプロパティや OS の環境変数でも指定可能。
- すると、指定されたプロファイルに該当するプロパティファイルが読み込まれる。

## 同じプレフィックスを持つプロパティを Bean にマッピングする
```properties:application.properties
person.firstName=Sato
person.last-name=Taro
person.age=18
```

```java:Person.java
package sample.springboot;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix="person")
public class Person {
    
    private String firstName;
    private String lastName;
    private int age;
    
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void hello() {
        System.out.println(firstName + " " + lastName + " : " + age);
    }
}
```

```java:Main.java
package sample.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
@EnableConfigurationProperties
public class Main {
    
    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Person person = ctx.getBean(Person.class);
            person.hello();
        }
    }
}
```

```text:実行結果
Sato Taro : 18
```

- `@ConfigurationProperties` を使うことで、特定のプレフィックスを持つプロパティたちを Bean にマッピングできる。
    - Bean にはセッターメソッドが必要になる。
    - フィールドの名前は、キャメルケース以外にもハイフン区切りやスネークケースでもマッピングしてくれる。
- この仕組を有効にするには、 `@EnableConfigurationProperties` アノテーションを追加する必要がある。
    - 厳密には、 `@Configuration` でアノテートされたクラスに追加する。

## Yaml を使用する
設定ファイルを application.yaml にすれば、 Yaml が使用できる。

```yaml:application.yaml
aaa:
    bbb:
        ccc: Hoge
        ddd: Fuga
    eee:
        fff: Piyo
```

```java:Main.java
package sample.springboot;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.hello();
        }
    }
    
    @Value("${aaa.bbb.ccc}") private String ccc;
    @Value("${aaa.bbb.ddd}") private String ddd;
    @Value("${aaa.eee.fff}") private String fff;
    
    public void hello() {
        System.out.println("ccc=" + ccc);
        System.out.println("ddd=" + ddd);
        System.out.println("fff=" + fff);
    }
}
```

```text:実行結果
ccc=Hoge
ddd=Fuga
fff=Piyo
```

### リストをマッピングする
```yaml:application.yaml
myconf:
    list:
        - hoge
        - fuga
        - piyo
```

```java:MyConf.java
package sample.springboot;

import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix="myconf")
public class MyConfig {
    
    private List<String> list;

    public List<String> getList() {
        return list;
    }

    public void setList(List<String> list) {
        this.list = list;
    }
}
```

```java:Main.java
package sample.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
@EnableConfigurationProperties
public class Main {
    
    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            MyConfig conf = ctx.getBean(MyConfig.class);
            System.out.println(conf.getList());
        }
    }
}
```

```text:実行結果
[hoge, fuga, piyo]
```

- Bean へのマッピングを利用すれば、 List へのマッピングも可能になる。


## プロパティファイル以外から設定値を渡す
```java:Main.java
package sample.springboot;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            Main m = ctx.getBean(Main.class);
            m.hello();
        }
    }
    
    @Value("${value}") private String value;
    
    public void hello() {
        System.out.println("value=" + value);
    }
}
```

### コマンドライン引数
```bash:
$ java -jar build/libs/spring-boot-sample.jar --value=commandline

value=commandline
```

- `--<プロパティ名>=<値>` で、コマンドライン引数から設定値を渡せる。

### Java のシステムプロパティ
```bash:
$ java -Dvalue=systemproperty -jar build/libs/spring-boot-sample.jar

value=systemproperty
```

- `--D<プロパティ名>=<値>` で、システムプロパティから設定値を渡せる。

### OS の環境変数
```bash:
$ set value=osenvironment

$ java -jar build/libs/spring-boot-sample.jar

value=osenvironment
```

- ※OS は Windows です。

### デフォルトプロパティ
```java:Main.java
package sample.springboot;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) {
        Map<String, Object> properties = new HashMap<>();
        properties.put("value", "default property");
        
        SpringApplication app = new SpringApplication(Main.class);
        app.setDefaultProperties(properties);
        
        try (ConfigurableApplicationContext ctx = app.run(args)) {
            Main m = ctx.getBean(Main.class);
            m.hello();
        }
    }
    
    @Value("${value}") private String value;
    
    public void hello() {
        System.out.println("value=" + value);
    }
}
```

```text:実行結果
value=default property
```

- `SpringApplication#setDefaultProperties(Map<String, Object>)` で、デフォルトの設定を指定できる。

### 優先順位
プロパティファイルの場合と同じように、設定値の渡し方には優先順位があり、優先度が上位の方法が、下位の方法で指定された設定値を上書きする。

優先順位は以下のようになっている。

1. コマンドライン引数
1. JNDI の `java:comp/env` から取得した属性
1. システムプロパティ
1. OS の環境変数
1. jar の外にあるプロファイル指定されたプロパティファイル
1. jar の中にあるプロファイル指定されたプロパティファイル
1. jar の外にあるプロパティファイル
1. jar の中にあるプロパティファイル
1. `@PropertySource` で指定されたプロパティファイル
1. デフォルトプロパティ

数字が小さいほうが、優先度が高い。

# メール送信
Gmail を使ってメールを送信してみる。

## アプリパスワードの生成
２段階認証を有効にしている場合は、先にアプリパスワードを生成しておく必要がある。

[アプリパスワードの生成](https://support.google.com/accounts/answer/185833)

## 依存 jar を取得する
[Java Mail](https://java.net/projects/javamail/pages/Home) と [JavaBeans Activation Framework](http://www.oracle.com/technetwork/java/jaf11-139815.html) をダウンロードしてくる。

## 依存関係の追加
```diff:build.gradle
dependencies {
    compile 'org.springframework.boot:spring-boot-starter'
+   compile 'org.springframework.boot:spring-boot-starter-mail'
+   compile fileTree(dir: 'libs', include: '*.jar')
}
```

```text:フォルダ構成
|-build.gradle
|-libs/
|  |-activation.jar
|  `-javax.mail.jar
`-src/
```

## 実装
```properties:application.properties
spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=<Gmail のアドレス>
spring.mail.password=<パスワード※>
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
```

※２段階認証を有効にしている場合は「アプリパスワード」を、そうでない場合は普通のログインパスワードを設定する

```java:Main.java
package sample.springboot;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.mail.MailSender;
import org.springframework.mail.SimpleMailMessage;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            ctx.getBean(Main.class).sendMail();
        }
    }
    
    @Autowired
    private MailSender sender;
    
    public void sendMail() {
        SimpleMailMessage msg = new SimpleMailMessage();
        
        msg.setFrom("test@mail.com");
        msg.setTo("宛先メールアドレス");
        msg.setSubject("Send mail from Spring Boot");
        msg.setText("Spring Boot からメールを送信するよ！");
        
        this.sender.send(msg);
    }
}
```

## 受信結果
![spring-boot.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/d5e6dfd9-0bc6-374b-3d65-495dc70045cc.jpeg)


# ロギング
ロギングには、 Commons Logging, Log4j, Slf4j, Logback などなど色々使えるようになっているっぽい。

```java:Main.java
package sample.springboot;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
@EnableConfigurationProperties
public class Main {
    
    private static final Logger logger = LoggerFactory.getLogger(Main.class);
    
    public static void main(String[] args) {
        try (ConfigurableApplicationContext ctx = SpringApplication.run(Main.class, args)) {
            logger.error("error log");
            logger.warn("warn log");
            logger.info("info log");
            logger.debug("debug log");
            logger.trace("trace log");
        }
    }
}
```

```text:実行結果
2015-04-29 17:31:25.023 ERROR 8872 --- [           main] sample.springboot.Main                   : error log
2015-04-29 17:31:25.023  WARN 8872 --- [           main] sample.springboot.Main                   : warn log
2015-04-29 17:31:25.023  INFO 8872 --- [           main] sample.springboot.Main                   : info log
```

- デフォルトでは、 INFO レベル以上だけが出力される。
- フォーマットは、`日付 エラーレベル PID --- [スレッド名] ロガー名 : ログメッセージ`。

## ファイルに出力する
デフォルトでは標準出力にしかログが出力されないが、ログファイルを指定すればファイルにも出力されるようになる。
ログファイルは、 10MB ずつローテーションされる。

### ファイル名指定
```bash:
$ java -jar build/libs/spring-boot-sample.jar --logging.file=sample.log

$ dir /b *.log
sample.log
```

- `logging.file` で、出力するファイルの名前を指定できる。
    - 便宜上コマンドライン引数で指定しているが、[プロパティファイル](#外部設定externalized-configurationを利用する) などでも指定できる。
- ファイルの出力先にディレクトリが存在しない場合は、勝手に作成される。

### フォルダ指定
```bash:
$ java -jar build/libs/spring-boot-sample.jar --logging.path=logs

$ dir /b logs
spring.log
```

- `logging.path` で、ログファイルの出力先を指定できる。
- ログファイルの名前は、 `spring.log` になる。
- ディレクトリが存在しない場合は勝手に作成される。

## ロガーごとにログレベルを指定する
```bash:
$ java -jar build/libs/spring-boot-sample.jar --logging.level.sample.springboot.Main=TRACE

2015-04-29 18:14:17.969 ERROR 8288 --- [           main] sample.springboot.Main                   : error log
2015-04-29 18:14:17.970  WARN 8288 --- [           main] sample.springboot.Main                   : warn log
2015-04-29 18:14:17.970  INFO 8288 --- [           main] sample.springboot.Main                   : info log
2015-04-29 18:14:17.970 DEBUG 8288 --- [           main] sample.springboot.Main                   : debug log
2015-04-29 18:14:17.970 TRACE 8288 --- [           main] sample.springboot.Main                   : trace log
```

- `logging.level.<ロガー>=<ログレベル>` で、ロガーごとのログレベルを指定できる。
- ロガー名が FQCN になるようにしているなら、 `--logging.level.sample.springboot=DEBUG` のようにしてパッケージ単位での指定もできる。

# エンドポイント
`spring-boot-starter-actuator` を依存関係に追加すると、システムの状態を Web API で取得できるようになる。

```groovy:build.gradle
dependencies {
    compile 'org.springframework.boot:spring-boot-starter-web'
    compile 'org.springframework.boot:spring-boot-starter-actuator'
}
```

```java:Main.java
package sample.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) throws Exception {
        SpringApplication.run(Main.class, args);
    }
}
```

アプリケーションを起動して、いくつかの URL にアクセスしてみる。

```bash:
$ curl http://localhost:8080/health
{"status":"UP"}

$ curl http://localhost:8080/metrics
{"mem":253440,"mem.free":127785,"processors":8,"instance.uptime":51033,"uptime":53546,"systemload.average":-1.0,"heap.committed":253440,"heap.init":262144,"heap.used":125654,"heap":3717632,"threads.peak":16,"threads.daemon":14,"threads":16,"classes":5490,"classes.loaded":5490,"classes.unloaded":0,"gc.ps_scavenge.count":3,"gc.ps_scavenge.time":39,"gc.ps_marksweep.count":1,"gc.ps_marksweep.time":44,"httpsessions.max":-1,"httpsessions.active":0,"counter.status.200.health":1,"counter.status.200.metrics":1,"gauge.response.health":47.0,"gauge.response.metrics":23.0}
```

以下が、エンドポイントの一例。

|     id     |                                         説明                                        |
|------------|-------------------------------------------------------------------------------------|
| `dump`     | スレッドダンプ                                                                      |
| `env`      | システムプロパティ・環境変数・プロパティファイルの設定値など                        |
| `health`   | アプリケーションの状態                                                              |
| `metrics`  | メモリ使用率やスレッド数、 GC 回数などなど                                          |
| `trace`    | 最近のアクセス履歴                                                                  |
| `shutdown` | POST メソッドでアクセスすることで、アプリケーションを停止できる。デフォルトは無効。 |

他にも [いろいろある](http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/html/production-ready-endpoints.html)。

# その他
## 起動時のバナーを表示させないようにする
**1.3.2 で確認**

```java:Main.java
package sample.springboot;

import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Main.class);
        app.setBannerMode(Banner.Mode.OFF);
        
        try (ConfigurableApplicationContext ctx = app.run(args)) {
            Main m = ctx.getBean(Main.class);
            m.hello();
        }
    }
    
    public void hello() {
        System.out.println("Hello Spring Boot!!");
    }
}
```

- `SpringApplication#setBannerMode(Banner.Mode)` に `Banner.Mode.OFF` を設定すると、バナー表示がなくなる。

## war で出力する
war で出力して、 Tomcat などの既存の AP サーバーにデプロイできるようにする。

### 実装
```groovy:build.gradle
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.2.5.RELEASE'
    }
}

apply plugin: 'war'
apply plugin: 'spring-boot'

sourceCompatibility = '1.8'
targetCompatibility = '1.8'

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.springframework.boot:spring-boot-starter-web'
    providedCompile 'org.springframework.boot:spring-boot-starter-tomcat'
}

war {
    baseName = 'spring-boot-war'
}
```

- `war` プラグインを読み込む。
- デフォルトで組み込みサーバーとして使用している Tomcat の依存関係を `providedCompile` に変更。

```java:Main.java
package sample.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;

@SpringBootApplication
public class Main extends SpringBootServletInitializer {
    
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(Main.class);
    }
    
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }
}
```

- main メソッドを定義していたクラスは、以下のように修正する。
    - `SpringBootServletInitializer` を継承する。
    - `configure(SpringApplicationBuilder)` をオーバーライドする。

```java:SampleResource.java
package sample.springboot;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/sample")
public class SampleResource {
    
    @RequestMapping(method=RequestMethod.GET)
    public String hello() {
        return "Hello Spring Boot!!";
    }
}
```

- 動作確認用のリソースクラス。

### 動作確認
```bash:warをビルドする
$ gradle war
```

`build/libs` の下に `spring-boot-war.jar` が出力されるので、 Tomcat にデプロイする。

```bash:curlで動作確認
$ curl http://localhost:8080/spring-boot-war/sample
Hello Spring Boot!!
```

割と簡単に war 化できた。

**参考**

- [59.4 Packaging executable jar and war files | Spring Boot Reference Guide](http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#build-tool-plugins-gradle-packaging)
- [74.1 Create a deployable war file | Spring Boot Reference Guide](http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-create-a-deployable-war-file)
- [Spring BootアプリケーションをビルドしてWARファイルにする - M12i.](http://m12i.hatenablog.com/entry/2014/11/30/002952)

## 限定子
Boot というよりかは、 Spring 自体の使い方。
Spring にも CDI の限定子と同じようなものが用意されている。

### 名前で指定する

**実装**

```java:MyInterface.java
package sample.springboot;

public interface MyInterface {
}
```

```java:Hoge.java
package sample.springboot;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
@Qualifier("hoge")
public class Hoge implements MyInterface {
}
```

```java:Fuga.java
package sample.springboot;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
@Qualifier("fuga")
public class Fuga implements MyInterface {
}
```

```java:Main.java
package sample.springboot;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) {
        try (ConfigurableApplicationContext context = SpringApplication.run(Main.class, args)) {
            Main m = context.getBean(Main.class);
            System.out.println("hoge = " + m.hoge.getClass());
            System.out.println("fuga = " + m.fuga.getClass());
        }
    }
    
    @Autowired @Qualifier("hoge")
    private MyInterface hoge;
    
    @Autowired @Qualifier("fuga")
    private MyInterface fuga;
}
```

**動作確認**

```text:コンソール出力
hoge = class sample.springboot.Hoge
fuga = class sample.springboot.Fuga
```

**説明**

- `@Qualifier` アノテーションを使って、ビーンの名前を指定できる。
- `@Autowired` と合わせて `@Qualifier` で名前を指定することで、特定のビーンをインジェクションできる。
- CDI でいうと `@Named` 的な使い方になる。

## 限定子を自作する

**実装**

```java:MyQualifier.java
package sample.springboot;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.beans.factory.annotation.Qualifier;

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface MyQualifier {
    MyType value();
}
```

```java:MyType.java
package sample.springboot;

public enum MyType {
    HOGE,
    FUGA,
}
```

```java:Hoge.java
package sample.springboot;

import org.springframework.stereotype.Component;

@Component
@MyQualifier(MyType.HOGE)
public class Hoge implements MyInterface {
}
```

```java:Fuga.java
package sample.springboot;

import org.springframework.stereotype.Component;

@Component
@MyQualifier(MyType.FUGA)
public class Fuga implements MyInterface {
}
```

```java:Main.java
package sample.springboot;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Main {
    
    public static void main(String[] args) {
        try (ConfigurableApplicationContext context = SpringApplication.run(Main.class, args)) {
            Main m = context.getBean(Main.class);
            System.out.println("hoge = " + m.hoge.getClass());
            System.out.println("fuga = " + m.fuga.getClass());
        }
    }
    
    @Autowired @MyQualifier(MyType.HOGE)
    private MyInterface hoge;
    
    @Autowired @MyQualifier(MyType.FUGA)
    private MyInterface fuga;
}
```

**動作確認**

```text:コンソール出力
hoge = class sample.springboot.Hoge
fuga = class sample.springboot.Fuga
```

**説明**

- `@Qualifier` で自作のアノテーションをアノテートすることで、限定子を自作できる。
- CDI のカスタム限定子と同じ要領で使えるっぽい。

# 参考
- [Getting Started · Building an Application with Spring Boot](http://spring.io/guides/gs/spring-boot/)
- [Spring Boot Reference Guide](http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/html/)
- [Amazon.co.jp： はじめてのSpring Boot―「Spring Framework」で簡単Javaアプリ開発 (I・O BOOKS): 槇 俊明: 本][3]
- [Spring関連の訳出記事まとめ - M12i.](http://m12i.hatenablog.com/entry/2014/11/24/121347)
- [BootstrapやjQueryをWebJarsで簡単管理 - DENの思うこと](http://den2sn.hatenablog.com/entry/2013/06/01/005402)
- [Spring Data JPA - Reference Documentation](http://docs.spring.io/spring-data/jpa/docs/1.8.0.RELEASE/reference/html/#repositories.query-methods.query-creation)
- [Spring Framework Reference Documentation](http://docs.spring.io/spring/docs/4.2.0.BUILD-SNAPSHOT/spring-framework-reference/htmlsingle/#mail)
- [どこよりも早い Spring Boot 1.2 解説 #渋谷Java](http://www.slideshare.net/makingx/spring-boot12)

[1]: http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#howto-build-an-executable-archive-with-ant
[2]: http://www.webjars.org/classic
[3]: http://www.amazon.co.jp/%E3%81%AF%E3%81%98%E3%82%81%E3%81%A6%E3%81%AESpring-Boot%E2%80%95%E3%80%8CSpring-Framework%E3%80%8D%E3%81%A7%E7%B0%A1%E5%8D%98Java%E3%82%A2%E3%83%97%E3%83%AA%E9%96%8B%E7%99%BA-I%E3%83%BBO-BOOKS/dp/4777518655