Kotlin with Spring Boot 2.0で簡単なRest APIを実装する

# 概要

以前に[Kotlin with Spring Boot 1.5で簡単なRest APIを実装する] (https://qiita.com/rubytomato@github/items/7d4bb10ca3779ab3277c)というタイトルで書いた記事のSpring Boot 2.0版です。
~~現在(2018/2)の最新バージョンであるRC2を使用しています。~~

* 2018/3/1にSpring Boot 2.0.0 GAがリリースされました。([Spring Boot 2.0 goes GA] (https://spring.io/blog/2018/03/01/spring-boot-2-0-goes-ga))
* 2018/5/9にSpring Boot 2.0.2がリリースされましたのでソースコードおよび記事を修正しました。([Spring Boot 2.0.2] (https://spring.io/blog/2018/05/09/spring-boot-2-0-2))

ソースコードは[rubytomato/demo-kotlin-spring2] (https://github.com/rubytomato/demo-kotlin-spring2)にあります。

**環境**

* Windows 10 Professional
* Java 1.8.0_172
* Kotlin 1.2.31
* Spring Boot 2.0.2
    * Spring Framework 5.0.5
    * MySQL 5.7.19
    * Gradle 4.5.1
* IntelliJ IDEA 2017.3

**参考**

* [Spring Boot 2.0 Release Notes] (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Release-Notes)
* [Spring Boot with Java 9 and above] (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-with-Java-9-and-above)

**1.5から移行する場合**

[Spring Boot 2.0 Migration Guide] (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Migration-Guide)が用意されています。

# アプリケーション

アプリケーションのひな型は[Spring Initializr](https://start.spring.io/)で生成しました。
~~生成したbuild.gradleのkotlinのバージョンには1.2.20が指定されていますが、Spring Boot
 2.0.0は1.2.21までサポートしているので、この記事では1.2.21を使用しています。~~

## build.gradle

```groovy
buildscript {
	ext {
		kotlinVersion = "1.2.31"
		springBootVersion = "2.0.2.RELEASE"
	}
	repositories {
		mavenCentral()
	}
	dependencies {
		classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
		classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
		classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}")
		classpath("org.jetbrains.kotlin:kotlin-noarg:${kotlinVersion}")
	}
}

apply plugin: "kotlin"
apply plugin: "kotlin-spring"
apply plugin: "kotlin-jpa"
apply plugin: "org.springframework.boot"
apply plugin: "io.spring.dependency-management"
apply plugin: "kotlin-kapt"

group = "com.example"
version = "0.0.3-SNAPSHOT"
sourceCompatibility = 1.8
targetCompatibility = 1.8
compileKotlin {
	kotlinOptions {
		freeCompilerArgs = ["-Xjsr305=strict"]
		jvmTarget = "1.8"
	}
}
compileTestKotlin {
	kotlinOptions {
		freeCompilerArgs = ["-Xjsr305=strict"]
		jvmTarget = "1.8"
	}
}
compileJava {
	options.compilerArgs << "-Xlint:unchecked,deprecation"
}
compileTestJava {
	options.compilerArgs << "-Xlint:unchecked,deprecation"
}

repositories {
	mavenCentral()
}

dependencies {
	// for web
	compile("org.springframework.boot:spring-boot-starter-web")
	compile("com.fasterxml.jackson.module:jackson-module-kotlin")
	// for db
	compile("org.springframework.boot:spring-boot-starter-data-jpa")
	runtime("mysql:mysql-connector-java")
	// compile("javax.xml.bind:jaxb-api:2.1")
	// kotlin
	compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
	compile("org.jetbrains.kotlin:kotlin-reflect")
	// for dev
	kapt("org.springframework.boot:spring-boot-configuration-processor")
	runtime("org.springframework.boot:spring-boot-devtools")
	compileOnly("org.springframework.boot:spring-boot-configuration-processor")
	// for test
	testCompile("org.springframework.boot:spring-boot-starter-test")
	testCompile("com.h2database:h2")
}
```

### -Xjsr305 compiler flag

Spring Initializrで生成したプロジェクトでは、デフォルトでstrictモードになっています。
strictモードでは、Java側のオブジェクトのフィールドにNullableアノテーションが付いている場合、Kotlin側でNullableとして扱わないとコンパイルエラーになります。

例えば、次のコードは`result.body.id`の箇所がコンパイルエラーになります。
（strictモードでなければコンパイルエラーにはなりませんが警告はでます。）

```kotlin
@Test
fun `test get`() {
    val result: ResponseEntity<Memo> = testRestTemplate.getForEntity("/memo/1", Memo::class.java)

    assertThat(result.body.id).isEqualTo(1L)
    // Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type Memo? :compileTestKotlin FAILED
}
```

これはbodyフィールドにNullableアノテーションが付いてるためです。

```java
public class HttpEntity<T> {

    @Nullable
    private final T body;

}
```

コンパイルを通すには次のようにNullableとして扱います。

```kotlin
assertThat(result.body?.id).isEqualTo(1L)
// もしくはresult.body!!.id
```

### 依存性管理

依存性管理にプラグイン`io.spring.dependency-management`が必要になりました。

### javax.xml.bind

コメントアウトしてあるjavax.xml.bind:jaxb-apiは、Java9のときに必要になります。(原因はhttps://github.com/spring-projects/spring-boot/issues/11205)

### json

2.0からspring-boot-starter-jsonというjson用のstarterが用意されています。
spring-boot-starter-webの依存関係に含まれているので明示的に指定する必要はありませんが、kotlinでは別途com.fasterxml.jackson.module:jackson-module-kotlinが必要になります。

## application.yml

```yml
spring:
  application:
    name: kotlin with spring-boot 2.0
  output:
    ansi:
      enabled: detect
  profiles:
    active: dev
  datasource:
    url: jdbc:mysql://localhost:3306/demo_db?useSSL=false
    username: demo_user
    password: demo_pass
    hikari:
      connection-test-query: select 1
      connection-timeout: 10000
      maximum-pool-size: 2
      minimum-idle: 2
  jpa:
    open-in-view: true
    properties:
      hibernate:
        show_sql: true
        format_sql: true
        use_sql_comments: true
        generate_statistics: true
  jackson:
    serialization:
      write-dates-as-timestamps: false
      write-durations-as-timestamps: true

server:
  servlet:
    context-path: /app
  port: 9000

logging.file: demo.log
logging:
  file:
    max-size: 50MB
    max-history: 10
  level:
    root: info
    org.springframework: info
    org.hibernate: info
    org.hibernate.SQL: debug
    com.example.demo: debug
```

2.0からデフォルトのデータソースがHikariCPになっています。
HikariCPのプロパティを設定するには`spring.datasource.hikari.*`に指定します。

また、application.properties(.yml)ファイルは名前空間の変更などが行われているので、1.5からの移行をサポートするモジュールが用意されています。
使い方は以下のモジュールを組み込むだけです。組み込むと書き換えが必要なプロパティがあればコンソールに警告を表示し、一時的に新しいプロパティへ変換して起動してくれます。

***Maven***

```xml
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-properties-migrator</artifactId>
</dependency>
```

***Gradle***

```groovy
runtime("org.springframework.boot:spring-boot-properties-migrator")
```

## アプリケーションの起動クラス

2.0からrunApplicationという関数が用意されています。

```kotlin
package com.example.demokotlin2

@SpringBootApplication
class DemoKotlin2Application

fun main(args: Array<String>) {
    runApplication<DemoKotlin2Application>(*args)
}
```

## エンティティとリポジトリ

リポジトリはSpring Data Jpaのバージョンが上がったため若干変更点がありました。

**エンティティ**

1.5から変更はありませんでした。

```kotlin
package com.example.demokotlin2.entity

import java.io.Serializable
import java.time.LocalDateTime
import javax.persistence.*

@Entity
@Table(name = "memo")
data class Memo(
    @Column(name = "title", nullable = false)
    var title: String,
    @Column(name = "description", nullable = false)
    var description: String,
    @Column(name = "done", nullable = false)
    var done: Boolean = false,
    @Column(name = "updated", nullable = false)
    var updated: LocalDateTime = LocalDateTime.now(),
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Long = 0
) : Serializable
```

**リポジトリ**

この例ではJpaRepositoryインタフェースを実装していますが、このインタフェースで定義されているメソッドのシグネチャがいくつか変わっているので、1.5から移行する場合はコードの修正が必要になるかもしれません。

```kotlin
package com.example.demokotlin2.repository

import com.example.demokotlin2.entity.Memo
import org.springframework.data.jpa.repository.JpaRepository

interface MemoRepository : JpaRepository<Memo, Long>
```

* シグネチャの変更点は補足欄にまとめてあります。

## サービス

1.5と比べて特筆すべき変更点はありませんでした。

```kotlin
package com.example.demokotlin2.service

import com.example.demokotlin2.entity.Memo
import org.springframework.data.domain.Pageable

interface MemoService {
    fun findById(id: Long): Memo?
    fun findAll(page: Pageable): List<Memo>
    fun store(memo: Memo)
    fun removeById(id: Long)
}
```

```kotlin
package com.example.demokotlin2.service.impl

import com.example.demokotlin2.entity.Memo
import com.example.demokotlin2.repository.MemoRepository
import com.example.demokotlin2.service.MemoService
import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
class MemoServiceImpl(
    private val repository: MemoRepository) : MemoService {

    @Transactional(readOnly = true)
    override fun findById(id: Long): Memo? {
        return repository.findById(id).orElseGet(null)
    }

    @Transactional(readOnly = true)
    override fun findAll(page: Pageable): List<Memo> {
        return repository.findAll(page).content
    }

    @Transactional(timeout = 10)
    override fun store(memo: Memo) {
        repository.save(memo)
    }

    @Transactional(timeout = 10)
    override fun removeById(id: Long) {
        repository.deleteById(id)
    }

}
```

## コントローラ

1.5と比べて特筆すべき変更点はありませんでした。

```kotlin
package com.example.demokotlin2.controller

import com.example.demokotlin2.entity.Memo
import com.example.demokotlin2.service.MemoService
import org.springframework.data.domain.Pageable
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*

@RestController
@RequestMapping(path = ["memo"])
class MemoController(
    private val service: MemoService) {

    @GetMapping(path = ["{id}"], produces = [MediaType.APPLICATION_JSON_UTF8_VALUE])
    fun id(@PathVariable(value = "id") id: Long): ResponseEntity<Memo> {
        val memo = service.findById(id)
        return if (memo != null) {
            ResponseEntity.ok(memo)
        } else {
            ResponseEntity(HttpStatus.NOT_FOUND)
        }
    }

    @GetMapping(path = ["list"], produces = [MediaType.APPLICATION_JSON_UTF8_VALUE])
    fun list(page: Pageable): ResponseEntity<List<Memo>> {
        return ResponseEntity.ok(service.findAll(page))
    }

    @PostMapping(produces = [MediaType.TEXT_PLAIN_VALUE], consumes = [MediaType.APPLICATION_JSON_UTF8_VALUE])
    fun store(@RequestBody memo: Memo): ResponseEntity<String> {
        service.store(memo)
        return ResponseEntity.ok("success")
    }

    @DeleteMapping(path = ["{id}"], produces = [MediaType.TEXT_PLAIN_VALUE])
    fun remove(@PathVariable(value = "id") id: Long): ResponseEntity<String> {
        service.removeById(id)
        return ResponseEntity.ok("success")
    }

}
```

* ページング、ソートの設定をカスタマイズできるプロパティが追加されています。

```text
spring.data.web.pageable.*
spring.data.web.sort.*
```

たとえば、1ページに表示する最大件数は次のプロパティで設定できます。

```text
spring.data.web.pageable.default-page-size= 10
```

# APIの動作確認

**検索**

```
> curl -v http://localhost:9000/app/memo/1
```

```
> curl -v http://localhost:9000/app/memo/list
```

**ページング**

```
> curl -v http://localhost:9000/app/memo/list?page=0&size=10
```

**新規登録**

```
> curl -v -H "Content-Type:application/json" -d @memo.json -X POST http://localhost:9000/app/memo
```

**削除**

```
> curl -v -X DELETE http://localhost:9000/app/memo/1
```

# テストコード

## リポジトリの単体テスト

```kotlin
package com.example.demokotlin2.repository

import com.example.demokotlin2.entity.Memo
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager
import org.springframework.test.context.junit4.SpringRunner
import org.assertj.core.api.Assertions.*
import org.springframework.test.context.jdbc.Sql
import java.time.LocalDateTime

@RunWith(SpringRunner::class)
@DataJpaTest
class MemoRepositoryTests {

    @Autowired
    lateinit var entityManager: TestEntityManager

    @Autowired
    lateinit var sut: MemoRepository

    @Test
    @Sql(statements = ["INSERT INTO memo (id, title, description, done, updated) VALUES (1, 'memo test', 'memo description', FALSE, CURRENT_TIMESTAMP)"])
    fun findById() {
        val expected = entityManager.find(Memo::class.java, 1L)
        val actual = sut.findById(expected.id).orElseGet { null }
        assertThat(actual).isNotNull()
        assertThat(actual).isEqualTo(expected)
    }

    @Test
    fun save() {
        val updated = LocalDateTime.of(2018, 2, 19, 18, 12, 0)
        val expected = Memo(title = "test title", description = "test description", done = true, updated = updated)
        sut.saveAndFlush(expected)
        val actual = entityManager.find(Memo::class.java, expected.id)
        assertThat(actual).isEqualTo(expected)
    }

    @Test
    @Sql(statements = ["INSERT INTO memo (id, title, description, done, updated) VALUES (1, 'memo test', 'memo description', FALSE, CURRENT_TIMESTAMP)"])
    fun delete() {
        val expected = entityManager.find(Memo::class.java, 1L)
        sut.deleteById(expected.id)
        sut.flush()
        val actual = entityManager.find(Memo::class.java, expected.id)
        assertThat(actual).isNull()
    }

}
```

## サービスの単体テスト

```kotlin
package com.example.demokotlin2.service

import com.example.demokotlin2.entity.Memo
import com.example.demokotlin2.repository.MemoRepository
import com.example.demokotlin2.service.impl.MemoServiceImpl
import org.junit.*
import org.mockito.*
import org.mockito.Mockito.*
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.mockito.quality.Strictness
import org.assertj.core.api.Assertions.*
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.PageRequest
import java.util.*

class MemoServiceImplTests {

    @Rule
    @JvmField
    val rule: MockitoRule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS)

    @Mock
    lateinit var repository: MemoRepository

    @InjectMocks
    lateinit var sut: MemoServiceImpl

    @Test
    fun findById() {
        val expected = Optional.ofNullable(Memo(id = 11, title = "title", description = "description", done = true))
        Mockito.`when`(repository.findById(anyLong())).thenReturn(expected)

        val actual = sut.findById(11)

        assertThat(actual).isNotNull()
        assertThat(actual).isEqualTo(expected.get())

        verify(repository, times(1)).findById(anyLong())
    }

    @Test
    fun findAllWithEmptyList() {
        val pageable: PageRequest = PageRequest.of(0, 5)
        val expected = emptyList<Memo>()
        val page = PageImpl<Memo>(expected, pageable, 0)
        Mockito.`when`(repository.findAll(eq(pageable))).thenReturn(page)

        val actual = sut.findAll(pageable)

        assertThat(actual).hasSize(0)

        verify(repository, times(1)).findAll(eq(pageable))
    }

    @Test
    fun findAll() {
        val pageable: PageRequest = PageRequest.of(0, 5)
        val expected = listOf(
            Memo(id = 1, title = "title A", description = "desc A", done = true),
            Memo(id = 2, title = "title B", description = "desc B", done = false),
            Memo(id = 3, title = "title C", description = "desc C", done = true)
        )
        val page = PageImpl<Memo>(expected, pageable, 3)
        Mockito.`when`(repository.findAll(eq(pageable))).thenReturn(page)

        val actual = sut.findAll(pageable)

        assertThat(actual).hasSize(3)
        assertThat(actual).containsSequence(expected)

        verify(repository, times(1)).findAll(eq(pageable))
    }

}
```

## コントローラーのテスト

### 単体

```kotlin
package com.example.demokotlin2.controller

import com.example.demokotlin2.entity.Memo
import com.example.demokotlin2.service.MemoService
import com.fasterxml.jackson.databind.ObjectMapper
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.Mockito
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.http.MediaType
import org.springframework.test.context.junit4.SpringRunner
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.*
import org.springframework.test.web.servlet.result.MockMvcResultHandlers.print
import java.nio.charset.Charset

@RunWith(SpringRunner::class)
@WebMvcTest(MemoController::class)
class MemoControllerTests {

    @Autowired
    lateinit var mvc: MockMvc
    @Autowired
    lateinit var objectMapper: ObjectMapper
    @MockBean
    lateinit var memoService: MemoService

    private val contentType = MediaType(MediaType.APPLICATION_JSON.type,
            MediaType.APPLICATION_JSON.subtype, Charset.forName("utf8"))

    @Test
    fun getOne() {
        val expected = Memo(id=1L, title="test title", description="test description", done=true)
        val expectedJson = objectMapper.writeValueAsString(expected)
        Mockito.`when`(memoService.findById(anyLong())).thenReturn(expected)

        val builder = MockMvcRequestBuilders.get("/memo/{id}", 1)
                .accept(MediaType.APPLICATION_JSON_UTF8)

        val result = mvc.perform(builder)
                .andExpect(status().isOk)
                .andExpect(content().contentType(contentType))
                .andExpect(jsonPath("$").isNotEmpty)
                .andExpect(jsonPath("$.title").value(expected.title))
                .andExpect(jsonPath("$.description").value(expected.description))
                .andExpect(jsonPath("$.done").value(expected.done))
                .andDo(`print`())
                .andReturn()

        assertThat(result.response.contentAsString).isEqualTo(expectedJson)
    }

}
```

### 結合

```kotlin
package com.example.demokotlin2.controller

import com.example.demokotlin2.DemoKotlin2Application
import com.example.demokotlin2.entity.Memo
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.web.client.TestRestTemplate
import org.springframework.test.context.junit4.SpringRunner
import org.assertj.core.api.Assertions.*
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType

@RunWith(SpringRunner::class)
@SpringBootTest(classes = [DemoKotlin2Application::class],
    webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class MemoControllerTests {

    class MemoList : MutableList<Memo> by ArrayList()

    @Autowired
    lateinit var testRestTemplate: TestRestTemplate

    @Test
    fun getOne() {
        val result = testRestTemplate.getForEntity("/memo/1", Memo::class.java)

        assertThat(result).isNotNull
        assertThat(result.statusCode).isEqualTo(HttpStatus.OK)
        assertThat(result.headers.contentType).isEqualTo(MediaType.APPLICATION_JSON_UTF8)
        assertThat(result.body?.id).isEqualTo(1L)
    }

    @Test
    fun pagination() {
        val result = testRestTemplate.getForEntity("/memo/list?page={page}&size={size}", MemoList::class.java, 0, 3)

        assertThat(result).isNotNull
        assertThat(result.statusCode).isEqualTo(HttpStatus.OK)
        assertThat(result.headers.contentType).isEqualTo(MediaType.APPLICATION_JSON_UTF8)
        assertThat(result.body).hasSize(3)
    }

}
```

# 補足

## M1～M7、RC1,RC2のリリースノート

M1からRC2までのリリースノートの内容を大まかにまとめました。(2018/2/19)
ただ、すべてを確認、検証するには時間的に難しかったので、個人的に興味を引いたもの、影響の大きそうなものをピックアップしています。

### [Spring-Boot-2.0.0-M1-Release-Notes] (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0.0-M1-Release-Notes)

* [Spring Boot 2.0.0 M1 Configuration Changelog] (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0.0-M1-Configuration-Changelog)

#### ★baselineの変更

* Java8 or laterが必要です。
* Java6,7はサポートされません。

#### ★主要な依存関係のバージョンの変更

|component              |Spring Boot 1.5.10 |Spring Boot 2.0.0 |
|:----------------------|:------------------|:-----------------|
|Spring Framework       |4.3.14  |5.0.4 :arrow_up: |
|Spring Security        |4.2.4   |5.0.3 :arrow_up: |
|Spring Data Commons    |1.13.0  |2.0.5 :arrow_up: |
|Spring Data Jpa        |1.11.10 |2.0.5 :arrow_up: | 
|Aspectj                |1.8.13  |1.8.13 |
|javassist              |3.21.0-GA |3.22.0-GA :arrow_up: |
|Jetty                  |9.4.8   |9.4.8  |
|Tomcat                 |8.5.27  |8.5.28 :arrow_up: |
|Undertow               |1.4.22  |1.4.22 |
|Hibernate              |5.0.12  |5.2.14 :arrow_up: |
|Jackson                |2.8.10  |2.9.4 :arrow_up: |
|Gson                   |2.8.2   |2.8.2  |
|JUnit                  |4.12    |4.12   |
|JUnit-Jupiter          |        |5.1.0  |
|mockito                |1.10.19 |2.15.0 :arrow_up: |
|Maven                  |3.2+    |3.2+   |
|Gradle                 |2.9 or later |4 :arrow_up: |
|flyway                 |3.2.1   |5.0.7 :arrow_up: |
|liquibase              |3.5.3   |3.5.5 :arrow_up: |
|kotlin                 |        |1.2.21 |

#### Relaxed binding

* [Spring Boot Configuration Binding] (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-Configuration-Binding)

プロパティの緩やかなバインディング(Relaxed binding)が改善されています。
詳細は[Relaxed Binding 2.0] (https://github.com/spring-projects/spring-boot/wiki/Relaxed-Binding-2.0)に説明があります。

**プロパティの記述法**

プロパティの記法には次のものがあります。

|記法       |example           |desc |
|:----------|:-----------------|:---------------------------------------|
|camel-case |person.firstName  |                                        |
|kebab-case |person.first-name |.propertiesと.ymlファイルでの使用を推奨 |
|snake-case |person.first_name |kebab-caseの代替                        |
|upper-case |PERSON_FIRSTNAME  |システム環境変数を使用する場合に推奨    |

プロパティは(1)prefixと(2)nameに分けることができ、prefixとnameでは使える記法が異なります。

```text
my-app.my-module.foo.first-name= ruby
^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^  ^^^^
 |                    |           |
(1)                  (2)         (3)
```

1. prefix : kebab-case
1. name : kebab-case、camel-case、snake-case
1. value : プロパティの値

2.0以降は、ConfigurationPropertiesアノテーションのprefix属性の記述方法が変わっています。

例えば、次のプロパティをバインドするために

```text
my-app.my-module.foo.first-name= ruby
my-app.my-module.foo.last-name= tomato
my-app.my-module.foo.age= 10
```

prefixをkebab-case("my-app.my-module.foo")で記述することはできますが、

```kotlin:OK
@Component
@ConfigurationProperties(prefix = "my-app.my-module.foo")
data class FooConfig(
    var firstName: String = "",
    var lastName: String = "",
    var age: Int = 0
)
```

snake-case("my_app.my_module.foo")やcamel-case("myApp.myModule.foo")で記述すると実行時に例外をスローします。
1.5まではこれらの記法でも例外は起こりませんでしたが、プロパティの書き方に統一性がないと期待通りにバインドされませんでした。

```text:NG
Caused by: org.springframework.boot.context.properties.source.InvalidConfigurationPropertyNameException: Configuration property name 'my_app.my_module.foo' is not valid
```

プロパティを設定する側では、次の記法はすべてバインドできます。

```text:OK
my-app.my-module.foo.first-name= ruby
```

```text:OK
my_app.my_module.foo.first_name= ruby
```

```text:OK
myApp.myModule.foo.firstName= ruby
```

OS環境変数

```bat:OK
> SET MY_APP_MY_MODULE_FOO_FIRST_NAME= ruby
```

記法が混在した場合

```text:OK
my-app.my-module.foo.first_name= ruby
my-app.my-module.foo.firstName= ruby
my-app.my-module.foo.FIRST_NAME= ruby
```

[ConditionalOnProperty] (https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/api/org/springframework/boot/autoconfigure/condition/ConditionalOnProperty.html)アノテーションのprefixおよびname属性もkebab-caseで記述します。
ただし、こちらはsnake-caseやcamel-caseで記述しても例外がスローされないので注意が必要です。

```text:OK
@ConditionalOnProperty(prefix = "my-app.my-module.foo", name = ["test-flag"])
```

ちなみに、プロパティが特定の値のときにバインディングしたい場合はhavingValueで条件を指定します。
この例では"my-app.my-module.foo.test-flag= true"の時にBarPropertiesのオブジェクトがDIコンテナに登録されます。

```text
@Bean
@ConditionalOnProperty(prefix = "my-app.my-module.foo", name = ["test-flag"], havingValue = "true")
fun bar(foo: FooProperties): BarProperties {
    return BarProperties(foo.name)
}

data class BarProperties(val name: String)
```

またバインディングAPIは以下のように変更されました。

|     |package                                          |class             |
|:----|:------------------------------------------------|:-----------------|
|old  |[org.springframework.boot.bind] (https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/api/org/springframework/boot/bind/package-summary.html) |RelaxedDataBinder |
|new  |[org.springframework.boot.context.properties.bind] (https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/api/org/springframework/boot/context/properties/bind/package-summary.html) |Binder            |

**例外**

プロパティの中には、Springが利用するのではなく組み込んだライブラリへ渡すためのプロパティ定義があります。
たとえば下記のspring.jpa.properties.hibernate.*がそうですが、決められた記法で記述しないと認識されませんでした。
この例で言えばname部分(show_sql)はsnake-caseでなければ認識されなかったです。

```text
spring.jpa.properties.hibernate.show_sql= true
```

#### Feature Remove

* Remote CRaSH shell
* Spring Loaded
* Spring Boot CLIからテスト機能

#### 組み込みコンテナのパッケージの変更

|      |package                                          |interface |
|:-----|:------------------------------------------------|:---------|
|old   |org.springframework.boot.context.embedded        |[EmbeddedServletContainer] (https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/api/org/springframework/boot/context/embedded/EmbeddedServletContainer.html) |
|new   |org.springframework.boot.web.server              |WebServer                |

**Jetty**

|      |package                                          |class     |
|:-----|:------------------------------------------------|:---------|
|old   |[org.springframework.boot.context.embedded.jetty] (https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/api/org/springframework/boot/context/embedded/jetty/package-summary.html) |JettyEmbeddedServletContainerFactory |
|new   |[org.springframework.boot.web.embedded.jetty] (https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/api/org/springframework/boot/web/embedded/jetty/package-summary.html) |JettyServletWebServerFactory |
|      |                                                 |JettyReactiveWebServerFactory |

**Tomcat**

|      |package                                          |class     |
|:-----|:------------------------------------------------|:---------|
|old   |[org.springframework.boot.context.embedded.tomcat] (https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/api/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.html) |TomcatEmbeddedServletContainerFactory |
|new   |[org.springframework.boot.web.embedded.tomcat] (https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/api/org/springframework/boot/web/embedded/tomcat/package-summary.html) |TomcatServletWebServerFactory |
|      |                                                 |TomcatReactiveWebServerFactory |

**undertow**

|      |package                                           |class     |
|:-----|:-------------------------------------------------|:---------|
|old   |[org.springframework.boot.context.embedded.undertow] (https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/api/org/springframework/boot/context/embedded/undertow/package-summary.html) |UndertowEmbeddedServletContainerFactory|
|new   |[org.springframework.boot.web.embedded.undertow] (https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/api/org/springframework/boot/web/embedded/undertow/package-summary.html) |UndertowServletWebServerFactory |
|      |                                                  |UndertowReactiveWebServerFactory |

**Netty (補足)**

* Nettyは2.0から追加されたWebFluxのデフォルトのengineです。

|      |package                                           |class     |
|:-----|:-------------------------------------------------|:---------|
|new   |[org.springframework.boot.web.embedded.netty] (https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/api/org/springframework/boot/web/embedded/netty/package-summary.html) |NettyReactiveWebServerFactory |

#### ★デフォルトのdatasourceの変更

デフォルトのdatasourceが[tomcat] (https://tomcat.apache.org/tomcat-8.5-doc/jdbc-pool.html)から[HikariCP] (https://github.com/brettwooldridge/HikariCP)に変わっています。

##### HikariCP

**明示的に指定する場合** 

デフォルトなので明示的に指定する必要はありませんが、あえて記述すると次のようになります。

```properties:HikariCP
spring.datasource.type= com.zaxxer.hikari.HikariDataSource
```

HikariCPのプロパティ設定

```text
spring.datasource.hikari.*
```

|property name               |type    |default         |
|:---------------------------|:-------|:---------------|
|allow-pool-suspension       |Boolean |false           |
|auto-commit                 |Boolean |true            |
|catalog                     |String  |driver default  |
|connection-init-sql         |String  |none            |
|connection-test-query       |String  |none            |
|connection-timeout          |Long    |30000 ms        |
|data-source-class-name      |String  |none            |
|data-source-j-n-d-i         |String  |                |
|data-source-properties      |Map     |                |
|driver-class-name           |String  |none            |
|health-check-properties     |Map     |                |
|health-check-registry       |Object  |none (HikariCP 2.3.2+ supports Dropwizard HealthChecks.) |
|idle-timeout                |Long    |600000 ms       |
|initialization-fail-timeout |Long    |1 s             |
|isolate-internal-queries    |Boolean |false           |
|jdbc-url                    |String  |none            |
|leak-detection-threshold    |Long    |0 ms            |
|login-timeout               |Integer | (officialに記載なし) |
|max-lifetime                |Long    |1800000 ms      |
|maximum-pool-size           |Integer |10              |
|metric-registry             |Object  |none            |
|metrics-tracker-factory     |Factory | (officialに記載なし) |
|minimum-idle                |Integer |same as maximumPoolSize |
|password                    |String  |none            |
|pool-name                   |String  |auto-generated  |
|read-only                   |Boolean |false           |
|register-mbeans             |Boolean |false           |
|scheduled-executor          |service |none            |
|schema                      |String  |driver default  |
|transaction-isolation       |String  |driver default  |
|username                    |String  |none            |
|validation-timeout          |Long    |5000ms          |

##### Tomcat

**tomcatに戻したい場合**

***Maven***

```xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    <exclusions>
        <exclusion>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-jdbc</artifactId>
</dependency>
```

***Gradle***

```groovy
compile("org.springframework.boot:spring-boot-starter-data-jpa") {
    exclude group: "com.zaxxer", module: "HikariCP"
}
compile("org.apache.tomcat:tomcat-jdbc")
```

デフォルトの設定を上書きします。

```properties:Tocmcat
spring.datasource.type= org.apache.tomcat.jdbc.pool.DataSource
```

Tomcat Connection Poolのプロパティ設定

```text
spring.datasource.tomcat.*
```

**Common Attributes**

|property name               |type    |default             |
|:---------------------------|:-------|:-------------------|
|defaultAutoCommit           |boolean |JDBC driver default |
|defaultReadOnly             |boolean |                  |
|defaultTransactionIsolation |String  |JDBC driver default |
|defaultCatalog              |String  |                  |
|driverClassName             |String  |                  |
|username                    |String  |                  |
|password                    |String  |                  |
|maxActive                   |int     |100               |
|maxIdle                     |int     |same as maxActive |
|minIdle                     |int     |same as initialSize |
|initialSize                 |int     |10                |
|maxWait                     |int     |30000 ms          |
|testOnBorrow                |boolean |false             |
|testOnConnect               |boolean |false             |
|testOnReturn                |boolean |false             |
|testWhileIdle               |boolean |false             |
|validationQuery             |String  |                  |
|validationQueryTimeout      |int     |-1                |
|validatorClassName          |String  |null              |
|timeBetweenEvictionRunsMillis|int    |5000 ms           |
|numTestsPerEvictionRun      |int     |*Property not used |
|minEvictableIdleTimeMillis  |int     |60000 ms          |
|accessToUnderlyingConnectionAllowed |boolean |          |
|removeAbandoned             |boolean |false             |
|removeAbandonedTimeout      |int     |60 s              |
|logAbandoned                |boolean |false             |
|connectionProperties        |String  |null              |
|poolPreparedStatements      |boolean |*Property not used |
|maxOpenPreparedStatements   |int     |*Property not used |

* Tomcat JDBC Enhanced Attributesは割愛します。

#### spring-boot-starter-webへの依存関係の削除

* spring-boot-starter-mustache
* spring-boot-starter-thymeleaf

これらのstarterからspring-boot-starter-webへの依存関係が削除されています。
具体的にはpom.xmlのdependencyから次の記述が削除されています。

```xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
```

#### WebFluxのサポート

* spring-boot-starter-webflux
* spring-boot-starter-reactor-netty (default web engine)

#### Reactive dataのサポート

Reactive対応のData Repositoryが追加されています。

* MongoDB (spring-boot-starter-data-mongodb-reactive)
* Redis (spring-boot-starter-data-redis-reactive)
* Cassandra (spring-boot-starter-data-cassandra-reactive)

#### Proxying strategyのデフォルトの変更

デフォルトがCGLIBになりました。設定の変更はproxy-target-classプロパティで行います。

```properties
spring.aop.proxy-target-class= true
```

* true : CGLIB subclass-based
* false : JDK interface-based

#### application.propertiesの名前空間の変更

**Servlet-specific server properties**

|old                              |new                                  |
|:--------------------------------|:------------------------------------|
|server.context-parameters.*      |server.servlet.context-parameters.*  |
|server.context-path              |server.servlet.context-path          |
|server.jsp.init-parameters.*     |server.servlet.jsp.init-parameters.* |
|server.jsp.registered            |server.servlet.jsp.registered        |
|server.path                      |server.servlet.path                  |

**Multipart configuration**

|old                              |new                                  |
|:--------------------------------|:------------------------------------|
|spring.http.multipart.*          |spring.servlet.multipart.*           |

#### webEnvironment flagの指定方法の変更

webfluxが追加されたことにより、新しいプロパティspring.main.web-application-typeが追加されています。

Spring Boot 1.5までは、webアプリケーションとして起動させない(組み込みwebコンテナが立ち上がらない)ようにするには、下記のように設定していましたが

```properties:deprecated
spring.main.web-environment= false
```

2.0以降は新しいプロパティspring.main.web-application-typeにnoneを指定します。

```properties:new
spring.main.web-application-type= none
```

このプロパティに指定できる値は次の3種類です。

* none
    * The application should not run as a web application and should not start an embedded web server
* servlet
    * The application should run as a servlet-based web application and should start an embedded servlet web server.
* reactive
    * The application should run as a reactive web application and should start an embedded reactive web server.

#### Mustache templateのデフォルト値変更

Mustacheテンプレートエンジンのテンプレートファイルのデフォルト拡張子が.htmlから.mustacheに変わっています。

```properties
spring.mustache.suffix= .mustache
```

#### ★Gradle Pluginの変更

こちらのブログ([Spring Boot's new Gradle plugin] (https://spring.io/blog/2017/04/05/spring-boot-s-new-gradle-plugin))にポストされたとおり、Gradle Pluginの機能が変更されています。

##### 実行可能なjar/warの生成

実行可能jar又はwarの生成は、従来bootRepackageタスクで行っていましたが、bootJar又はbootWarタスクに置き換えられました。

##### dependency-management-plugin

依存関係の管理を行う[dependency-management-plugin] (https://github.com/spring-gradle-plugins/dependency-management-plugin)を明示的に組み込む必要があります。

```groovy
apply plugin: 'io.spring.dependency-management'
```


### [Spring-Boot-2.0.0-M2-Release-Notes] (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0.0-M2-Release-Notes)

* [Spring Boot 2.0.0 M2 Configuration Changelog] (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0.0-M2-Configuration-Changelog)

#### spring-boot-starter-quartzの追加

[Quartz Job Scheduler] (http://www.quartz-scheduler.org/)というジョブスケジューラがspring-boot-starterとしてサポートされました。

#### Spring Data Web configuration

ページングやソートの設定がapplication.propertiesで出来るようになっています。

```properties:example
# SpringDataWebProperties
spring.data.web.pageable.default-page-size= 20
spring.data.web.pageable.max-page-size= 2000
# Whether to expose and assume 1-based page number indexes.
spring.data.web.pageable.one-indexed-parameters= false
spring.data.web.pageable.page-parameter= page
# General prefix to be prepended to the page number and page size parameters.
spring.data.web.pageable.prefix= ""
# Delimiter to be used between the qualifier and the actual page number and size properties.
spring.data.web.pageable.qualifier-delimiter= _
spring.data.web.pageable.size-parameter= size
spring.data.web.sort.sort-parameter= sort
```

* prefix
    * 各パラメータのprefixを指定します。
    * 例) prefix=p_ → パラメータ名はp_pageやp_sizeとなります。
* one-indexed-parameters
    * false、ページ番号の始まりを0とします。
        * page=0は1ページ目、page=1は2ページ目を指します。
    * true、ページ番号の始まりを1とします。
        * page=0とpage=1は同じ1ページ目、page=2は2ページ目を指します。
* qualifier-delimiter
    * qualifierとパラメータの区切り文字を指定します。
    * 1つのURLに2つ(以上)のページ情報を含めたい場合に利用します。

このプロパティはClass [SpringDataWebProperties] (https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/api/org/springframework/boot/autoconfigure/data/web/SpringDataWebProperties.html)に反映されます。

**サンプルコード**

```kotlin
import org.springframework.data.domain.Pageable
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RestController
@RequestMapping(path = ["index"])
class IndexController {

    // 例1
    @GetMapping
    fun index(pageable: Pageable): String {
        return pageable.toString()
    }

    // 例2
    @GetMapping(path = ["2"])
    fun index2(@Qualifier("foo") foo: Pageable
             , @Qualifier("bar") bar: Pageable): String {
        println("foo: ${foo}")
        println("bar: ${bar}")
        return "OK"
    }
}
```

例1

```text
http://localhost:9000/app/index

　　　↓

Page request [number: 0, size 20, sort: UNSORTED]
```

```text
http://localhost:9000/app/index?page=1&size=30&sort=id,desc&sort=name,asc

　　　↓

Page request [number: 1, size 30, sort: id: DESC,name: ASC]
```

例2

```text
http://localhost:9000/app/index/2?foo_page=1&foo_size=10&bar_page=3&bar_size=10

　　　↓

foo: Page request [number: 1, size 10, sort: UNSORTED]
bar: Page request [number: 3, size 10, sort: UNSORTED]
```

**注意**

[Spring Data Rest] (https://projects.spring.io/spring-data-rest/)に似たパラメータがあるので注意が必要です。

```properties
# DATA REST (RepositoryRestProperties)
spring.data.rest.default-page-size= # Default size of pages.
spring.data.rest.limit-param-name= # Name of the URL query string parameter that indicates how many results to return at once.
spring.data.rest.max-page-size= # Maximum size of pages.
spring.data.rest.page-param-name= # Name of the URL query string parameter that indicates what page to return.
spring.data.rest.sort-param-name= # Name of the URL query string parameter that indicates what direction to sort results.
```

#### JDBCTemplate configuration

JDBCTemplateの設定がapplication.propertiesで出来るようになっています。

```properties:example
spring.jdbc.template.fetch-size= -1
spring.jdbc.template.max-rows= -1
spring.jdbc.template.query-timeout=
```

このプロパティはClass [JdbcProperties] (https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/api/org/springframework/boot/autoconfigure/jdbc/JdbcProperties.html)に反映されます。

#### Cassandraのプーリングオプションのconfiguration

プーリングオプションの設定がapplication.propertiesで出来るようになっています。

```properties:example
spring.data.cassandra.pool.heartbeat-interval= 30s
spring.data.cassandra.pool.idle-timeout= 120s
spring.data.cassandra.pool.max-queue-size= 256
spring.data.cassandra.pool.pool-timeout= 5000ms
```

このプロパティはClass [CassandraProperties] (https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/api/org/springframework/boot/autoconfigure/cassandra/CassandraProperties.html)に反映されます。

#### ★spring-boot-starter-jsonの追加

jackson-databindの代わりになるspring-boot-starter-jsonが追加されています。

***Maven***

```xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-json</artifactId>
</dependency>
```

***Gradle***

```groovy
compile("org.springframework.boot:spring-boot-starter-json")
```

また、以下の依存関係を別途追加しなくてもよくなりました。

* jackson-datatype-jdk8
* jackson-datatype-jsr310
* jackson-module-parameter-names

#### spring-boot-starter-thymeleafの変更

spring-boot-starter-thymeleafにthymeleaf-extras-java8timeへの推移的依存関係が含まれるようになったので依存関係を別途追加しなくてもよくなりました。

#### Mongo clientのconfiguration

MongoClientSettingsBuilderCustomizerでMongoクライアントの設定ができるようになっています。

#### Test

Unit Test用のアノテーションが追加されています。

* JooqTest
* DataRedisTest



### [Spring-Boot-2.0.0-M3-Release-Notes] (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0.0-M3-Release-Notes)

* [Spring Boot 2.0.0 M3 Configuration Changelog] (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0.0-M3-Configuration-Changelog)

#### ★Maven Pluginの変更

ゴールにパラメータを指定する場合、パラメータ名のprefixに"spring-boot"を付加するようになっています。

**例**

profileを指定するパラメータは、1.5までは`run.profiles`でしたが

```text:old
mvn spring-boot:run -Drun.profiles=foo
```

2.0以降は`spring-boot.run.profiles`になります。

```text:new
mvn spring-boot:run -Dspring-boot.run.profiles=foo
```

#### DevToolsの変更

HTTP経由のリモートデバッグがサポートされなくなりました。



### [Spring-Boot-2.0.0-M4-Release-Notes] (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0.0-M4-Release-Notes)

* [Spring Boot 2.0.0 M4 Configuration Changelog] (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0.0-M4-Configuration-Changelog)

#### application.propertiesの名前空間の変更

**Liquibase and Flyway**

|old                              |new                                  |
|:--------------------------------|:------------------------------------|
|liquibase.*                      |spring.liquibase.*                   |
|flyway.*                         |spring.flyway.*                      |

#### Database initializer

Spring Batchが使用するデータベースの初期化をコントロールするプロパティが変わっています。
また、Spring Integration、Spring Session、QUARTZ SCHEDULERも同様の仕組みで初期化ができるようになりました。

指定できる値はDataSourceInitializationModeで定義されている次の3種類です。

* always
    * Always initialize the datasource.
* embedded
    * Only initialize an embedded datasource.
* never
    * Do not initialize the datasource.

**Spring Batch**

```text:deprecated
spring.batch.initializer.enabled= true
```

```text:new
spring.batch.initialize-schema= embedded
```

このプロパティはClass [BatchProperties] (https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/api/org/springframework/boot/autoconfigure/batch/BatchProperties.html)に反映されます。

**Spring Integration** 

```text:new
spring.integration.jdbc.initialize-schema= embedded
```

このプロパティはClass [IntegrationProperties] (https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/api/org/springframework/boot/autoconfigure/integration/IntegrationProperties.html)に反映されます。

**Spring Session**

```text:new
spring.session.jdbc.initialize-schema=embedded
```

このプロパティはClass [JdbcSessionProperties] (https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/api/org/springframework/boot/autoconfigure/session/JdbcSessionProperties.html)に反映されます。

**QUARTZ SCHEDULER**

```text:new
spring.quartz.jdbc.initialize-schema= embedded
```

このプロパティはClass [QuartzProperties] (https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/api/org/springframework/boot/autoconfigure/quartz/QuartzProperties.html)に反映されます。

#### compile flag

spring-boot-starter-parentを使うMavenプロジェクトでは`-parameters`コンパイルフラグがデフォルトで有効になります。

> [javac] (https://docs.oracle.com/javase/jp/8/docs/technotes/tools/windows/javac.html)
>
> Reflection APIのメソッドjava.lang.reflect.Executable.getParametersが取得できるように、コンストラクタやメソッドの仮パラメータ名を、生成されたクラス・ファイルに格納します。

#### ★Support

* Java9



### [Spring-Boot-2.0.0-M5-Release-Notes] (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0.0-M5-Release-Notes)

* [Spring Boot 2.0.0 M5 Configuration Changelog] (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0.0-M5-Configuration-Changelog)

#### Maven Surefire pluginのバージョンアップ

[Maven Surefire Plugin](http://maven.apache.org/surefire/maven-surefire-plugin/)のinclude/excludeの設定が変更されています。
下記は1.5までのデフォルトの設定です。2.0では利用するSurefire Pluginのバージョンが上がっていてデフォルトの設定が変わっています。

```xml:old
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <includes>
             <include>**/*Tests.java</include>
             <include>**/*Test.java</include>
        </includes>
        <excludes>
            <exclude>**/Abstract*.java</exclude>
        </excludes>
    </configuration>
</plugin>
```

また、JUnit5を利用する場合は使用するMaven Surefire pluginのバージョンを2.19.1に下げる必要があるようです。

* [Inclusions and Exclusions of Tests] (http://maven.apache.org/surefire/maven-surefire-plugin/examples/inclusion-exclusion.html)

**プラグインのバージョン状況**

デフォルトで利用するプラグインのバージョンをまとめました。

|plugin       |1.5.10   |2.0.0.RC2 |
|:------------|:--------|:---------|
|clean        |2.6.1    |3.0.0 :arrow_up: |
|compiler     |3.1      |3.7.0 :arrow_up: |
|deploy       |2.8.2    |2.8.2 |
|install      |2.5.2    |2.5.2 |
|jar          |2.6      |3.0.2 :arrow_up: |
|resources    |2.6      |3.0.1 :arrow_up: |
|site         |3.5.1    |3.6 :arrow_up: |
|surefire     |2.18.1   |2.20.1 :arrow_up: |
|spring-boot  |1.5.10   |2.0.0 :arrow_up: |
|build-helper |1.10     |-     |
|help         |2.2      |-     |
|xml          |1.0      |-     |

#### Gradle pluginの変更

bootRunタスクでアプリケーションの引数(args)とJVM引数(jvmArgs)を指定できるようになっています。

**例**

```groovy
bootRun {
    args += ['-X', '-F']
    jvmArgs += ['-Dspring.profiles.active=dev']
}
```

#### Redisドライバーの変更

spring-boot-starter-redisで組み込むRedisドライバーのデフォルトが[Jedis] (https://github.com/xetorthio/jedis)から[Lettuce] (https://github.com/lettuce-io/lettuce-core)へ変わっています。

#### Mockito 1.x

Mockito 1.xでは@MockBeanと@SpyBeanを使ってオブジェクトの注入ができません。
spring-boot-starter-testを使っていない場合は、手動でMockito 2.xへアップグレードする必要があります。

#### Support

* [OAuth 2.0] (http://projects.spring.io/spring-security-oauth/)
* Java9
* [Micrometer 1.0.0-rc.2] (http://micrometer.io/)
* [JSON-B] (http://json-b.net/)

#### ★Spring Framework 5.0 GA

[Spring Framework 5.0 goes GA] (https://spring.io/blog/2017/09/28/spring-framework-5-0-goes-ga)

このマイルストーンから、Spring Framework 5.0 GA版が使用されています。



### [Spring-Boot-2.0.0-M6-Release-Notes] (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0.0-M6-Release-Notes)

* [Spring Boot 2.0.0 M6 Configuration Changelog] (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0.0-M6-Configuration-Changelog)

#### Gradle pluginの変更

BootRun、BootJar、BootWarタスクで、mainClassNameプロパティを利用するようになりました。
それまではそれぞれのタスクのプロパティで設定していました。

#### JPAのopen-in-viewプロパティの警告

デフォルト値はtrueですが、このプロパティの値を明示的にtrueに設定しないと起動時に警告メッセージが表示されます。

```properties
spring.jpa.open-in-view= true
```

**警告メッセージ**

```text
spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
```

#### Configuration location

2.0ではapplication.propertiesの検索パスを設定方法が変わっています。

1.5まではspring.config.locationに設定するパスはデフォルトの検索パスに追加されていました。
2.0からはspring.config.locationに設定するパスはデフォルトの検索パスを置き換えます。検索パスを追加したい場合はadditional-locationという新しいプロパティを使用します。

```text:new
spring.config.additional-location= # Config file locations used in addition to the defaults.
```

#### ConfigurationPropertiesの検証

ConfigurationPropertiesアノテーションを付けたオブジェクトのバリデーションを行うにはValidatedアノテーションが必要です。

**例**

次のようなプロパティをFooConfigクラスへバインドする場合

```properties
my-app.my-module.foo.first-name= ruby
#my-app.my-module.foo.last-name= tomato
#my-app.my-module.foo.age= 77
my-app.my-module.foo.bar-settings.max-height= 999
my-app.my-module.foo.bar-settings.min-height= 100
```

```kotlin
@Component
@ConfigurationProperties(prefix = "my-app.my-module.foo")
@Validated
class FooConfig {

    lateinit var firstName: String

    lateinit var lastName: String

    var age: Int? = null

    var barSettings: BarConfig = BarConfig()

    class BarConfig {
        lateinit var maxHeight: Integer
        lateinit var minHeight: Integer
        override fun toString(): String {
            return "BarConfig(maxHeight=$maxHeight, minHeight=$minHeight)"
        }
    }

    override fun toString(): String {
        return "FooConfig(firstName='$firstName', lastName='$lastName', age=$age, barSettings=$barSettings)"
    }

}
```

Validatedアノテーションが付いているとアプリケーション起動時に検証が行われ、NotNullなプロパティが初期化できなかった場合、エラーを出力して終了します。

```text
***************************
APPLICATION FAILED TO START
***************************

Description:

Failed to bind properties under 'my-app.my-module.foo.last-name' to java.lang.String:

    Reason: Unable to get value for property last-name

Action:

Update your application's configuration
```

Validatedアノテーションが付いていない場合、アプリケーションは起動しますがFooConfigのプロパティに最初にアクセスしたタイミングでエラーが発生します。

```text
kotlin.UninitializedPropertyAccessException: lateinit property lastName has not been initialized
```

##### Property Origins

プロパティがバインドできなかった場合（BindExceptionがスローされるなどの理由）、そのプロパティのソース(property source)が表示されるようになっています。

下記はコマンドラインから与えたプロパティ"-Dmy-app.my-module.foo.color=white"が、my-app.my-module.foo.colorにバインドできなかったときに表示されたメッセージです。
指定された"white"という値がEnum Color型のフィールドにバインドできなかったためですが、この値が"SystemProperties"から指定されたことがメッセージの"Origin"の行から確認できます。

```text
***************************
APPLICATION FAILED TO START
***************************

Description:

Failed to bind properties under 'my-app.my-module.foo.color' to com.example.demomavenspring2.type.Color:

    Property: my-app.my-module.foo.color
    Value: white
    Origin: "my-app.my-module.foo.color" from property source "systemProperties"
    Reason: Failed to convert from type [java.lang.String] to type [@javax.validation.constraints.NotNull com.example.demomavenspring2.type.Color] for value 'white'; nested exception is java.lang.IllegalArgumentException: No en
um constant com.example.demomavenspring2.type.Color.white

Action:

Update your application's configuration. The following values are valid:

    BLUE
    CYAN
    GREEN
    MAGENTA
    RED
    YELLOW
```

#### ★DataSource Initialization

データベースの初期化をコントロールするプロパティが変更されています。

```properties:deprecated
spring.datasource.initialize= true
```

新しいプロパティは、デフォルトでは組み込みデータベース(H2、HSQL、Derby)に対してのみ機能します。

```properties:new
spring.datasource.initialization-mode= embedded
```

指定できる値はDataSourceInitializationModeで定義されている次の3種類です。
alwaysを指定すると組み込みデータベースでなくても機能してしまうので注意が必要です。

* embedded
    * Only initialize an embedded datasource.
* always
    * always initialize the datasource.
* never
    * Do not initialize the datasource.

#### JPA mapping resources

マッピングリソースファイルを指定できるようになっています。

```text:new
spring.jpa.mapping-resources= # Mapping resources (equivalent to "mapping-file" entries in persistence.xml).
```

#### HTTP/2

HTTP/2の有効／無効化をプロパティで設定できるようになっています。
このマイルストーンではTomcatとUndertowが対応します。(JettyはRC1で対応）

```text:new
server.http2.enabled= true
```

HTTP/2が有効になったか確認するにはsslを有効にしてhttpsでアクセスする必要があります。

#### 自動コンフィグレーションと依存関係の管理から除外

プロジェクトで利用する場合はバージョンを明示する必要があります。

* [Spring Mobile] (http://projects.spring.io/spring-mobile/)

#### Kotlin

runApplicationというインライン関数が用意されています。

1.5まで

```kotlin
fun main(args: Array<String>) {
    SpringApplication.run(Application::class.java, *args)
}
```

2.0以降は次のようにも記述できます。

```kotlin
fun main(args: Array<String>) {
    runApplication<Application>(*args)
}
```

または

```kotlin
fun main(args: Array<String>) {
    runApplication<Application>(*args) {
        // initialize code
    }
}
```



### [Spring-Boot-2.0.0-M7-Release-Notes] (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0.0-M7-Release-Notes)

* [Spring Boot 2.0.0 M7 Configuration Changelog] (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0.0-M7-Configuration-Changelog)

#### Jacksonのシリアライズのデフォルト値の変更

serialization.write-dates-as-timestampsプロパティのデフォルト値がfalseに変わっています。

**サンプルコード**

次のデータクラスのインスタンスがどのようにシリアライズされるか確認しました。

```kotlin
data class DateTimeDemo(
    var lt: LocalTime = LocalTime.now(),
    var ld: LocalDate = LocalDate.now(),
    var dt: LocalDateTime = LocalDateTime.now(),
    var ym: YearMonth = YearMonth.now(),
    var md: MonthDay = MonthDay.now(),
    var dd: Duration = Duration.of(1, ChronoUnit.HOURS),
    var pp: Period = Period.ofDays(1),
    var ii: Instant = Instant.now()
) : Serializable
```

2.0のデフォルトの場合

```text
spring.jackson.serialization.write-dates-as-timestamps= false
spring.jackson.serialization.write-durations-as-timestamps= true
```

　　　↓

```text
{
    lt: "14:06:20.9023938",
    ld: "2018-02-16",
    dt: "2018-02-16T14:06:20.9023938",
    ym: "2018-02",
    md: "--02-16",
    dd: "PT1H",
    pp: "P1D",
    ii: "2018-02-16T05:06:20.904893500Z"
}
```

1.5のデフォルトの場合(jacksonのデフォルトでもあります)

```text
spring.jackson.serialization.write-dates-as-timestamps= true
spring.jackson.serialization.write-durations-as-timestamps= true
```

　　　↓

```text
{
    lt: [
        14,
        7,
        43,
        61545000
    ],
    ld: [
        2018,
        2,
        16
    ],
    dt: [
        2018,
        2,
        16,
        14,
        7,
        43,
        61545000
    ],
    ym: [
        2018,
        2
    ],
    md: "--02-16",
    dd: 3600,
    pp: "P1D",
    ii: 1518757663.0645459
}
```

#### Kotlin

spring-boot-starter-jsonからjackson-module-kotlinの推移的依存関係が削除されました。
Kotlinでjacksonを利用している場合は手動で依存関係を追加する必要があります。

***Maven***

```xml
<dependency>
    <groupId>com.fasterxml.jackson.module</groupId>
    <artifactId>jackson-module-kotlin</artifactId>
</dependency>
```

***Gradle***

```groovy
compile("com.fasterxml.jackson.module:jackson-module-kotlin")
```

#### Spring Data repositories enablement

reactive対応のData Repositoryの追加により、リポジトリの有効/無効化のプロパティが変わっています。

従来のプロパティではtrue/falseで設定します。

```text:deprecated
spring.data.cassandra.repositories.enabled=
spring.data.mongodb.repositories.enabled=
spring.data.redis.repositories.enabled= 
spring.data.couchbase.repositories.enabled=
```

Redis以外は新しいプロパティで、auto(デフォルト)、imperative、reactive、noneから選択します。
Redisは従来通りで変更はないようです。

```properties:new
spring.data.cassandra.repositories.type= auto
spring.data.mongodb.repositories.type= auto
spring.data.redis.repositories.enabled= true
spring.data.couchbase.repositories.type= auto
```

* auto
    * enables all repository types automatically based on their availability
* imperative
    * enables imperative repisitories
* reactive
    * enables reactive repositories
* none
    * enables no repositories

**Redisだけtypeプロパティが無い**

それぞれのstarter(spring-boot-starter-data-redis、spring-boot-starter-data-redis-reactive)のpom.xmlを確認すると、reactiveは従来版のエイリアスのような設定で違いはないようです。

#### 自動コンフィグレーションと依存関係の管理から除外

プロジェクトで利用する場合はバージョンを明示する必要があります。

* [Spring Social] (https://projects.spring.io/spring-social/)
* [commons-digester] (https://commons.apache.org/proper/commons-digester/)

#### Conditions evaluation delta on restart

DevToolsを有効にしたアプリケーションは、起動中にクラスパスが更新されると自動的に再起動(automatic restart)します。
クラスパスを更新させる方法はIDEによって異なります。Eclipseでは変更したファイルを保存したとき、IntelliJ IDEAではプロジェクトのビルド(<kbd>Ctrol + F9</kbd>)、ファイルの再コンパイル(<kbd>Ctrl +
 Shift + F9</kbd>)でも同じ効果があります。

再起動を制御するには次のプロパティを設定します。

```text
spring.devtools.restart.enabled= false
```

再起動時にアプリケーションの自動構成の変更が検出されると、その変更内容が「CONDITION EVALUATION DELTA」というレポートに出力されます。
（あくまでもautomatic restart時なので、停止＆起動ではレポートは出力されません）
レポートの出力を制御するには次のプロパティを設定します。

```text
spring.devtools.restart.log-condition-evaluation-delta= false
```

#### application.propertiesのDuration型

プロパティがJava Duration型の場合、"1h"のようにsuffixに単位を付けて指定することができるようになりました。

```properties:example
server.session.cookie.max-age= 1h
```

suffixを付けない場合は従来通りの単位になります。

```properties
# Maximum age of the session cookie. If a duration suffix is not specified, seconds will be used.
server.session.cookie.max-age= 3600
```

ただし、時間の長さを指定するプロパティでもDuration型を取らないパラメータもありますので注意が必要です。
たとえば次のmax-ageはLong型なのでsuffixを付けるとエラーになります。

```properties:NG
spring.datasource.tomcat.max-age= 30s
```

#### キャッシュにRedisを使ったときのデフォルト設定

キャッシュにredisを使用した場合のデフォルト値をapllication.propetiesで設定できるようになっています。

キャッシュにRedisを選択

```properties
spring.cache.type= redis
```

次のプロパティでデフォルトを設定できます。

```properties:example
spring.cache.redis.time-to-live= 120s
spring.cache.redis.cache-null-values= true
spring.cache.redis.use-key-prefix= true
spring.cache.redis.key-prefix= myCache_
```

このプロパティはClass [CacheProperties] (https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/api/org/springframework/boot/autoconfigure/cache/CacheProperties.html)に反映されます。

cache-null-valuesプロパティをfalseにした場合、nullをキャッシュしようとするとIllegalArgumentExceptionがスローされます。
この場合は次のようにnullを除外するように設定します。

```kotlin
@Cacheable(cacheNames = ["myCache"], unless="#result == null")
```

#### spring-boot-starter-data-couchbase-reactiveの追加

Reactive対応のData Repositoryにcouchbaseが追加されています。
(MongoDB、Redis、CassandraはM1で対応)

* spring-boot-starter-data-couchbase
* spring-boot-starter-data-couchbase-reactive



### [Spring Boot 2.0.0 RC1 Release Notes] (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0.0-RC1-Release-Notes)

* [Spring Boot 2.0.0 RC1 Configuration Changelog] (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0.0-RC1-Configuration-Changelog)
* [asciidoc] (https://github.com/spring-projects/spring-boot/tree/v2.0.0.RC1/spring-boot-project/spring-boot-docs/src/main/asciidoc)

#### spring-boot-starter-webへの依存関係の削除

spring-boot-starter-freemarkerからspring-boot-starter-webへの依存関係が削除されています。
(spring-boot-starter-mustache、spring-boot-starter-thymeleafはM1にて対応)

#### Spring MVC path matchingの変更

suffixパスマッチングのデフォルトの挙動が変わっています。
これに伴い新しいパラメータが追加されています。

```text:new
spring.mvc.content-negotiation.favor-parameter= false
spring.mvc.content-negotiation.favor-path-extension= false
spring.mvc.content-negotiation.parameter-name= 
```

1.5まで、たとえばこれらのリクエストはすべて

```text
localhost://app/product
localhost://app/product.json
localhost://app/product.xml
```

次のハンドラメソッドにマッピングされていましたが、2.0以降はデフォルトではマッピングされなくなります。(404のNot Foundになります)

```text
@GetMapping(path = ["/app/product"])
fun product(): String {

    //... 省略

}
```

#### application.propertiesの名前空間の変更

|old                   |new                  |
|:---------------------|:--------------------|
|banner.*              |spring.banner.*      |
|spring.metrics.*      |management.metrics.* |
|server.*              |server.servlet.*     |

#### ★Legacy properties migration

プロパティファイルの構成が大きく変わっているのでspring-boot-properties-migratorというプロパティファイルの移行モジュールが用意されています。
このモジュールを組み込むと、アプリケーション起動時に古いプロパティがあるとワーニングを表示し、且つ新しい構成に一時的に変換して起動してくれます。
なお、application.propertiesファイルをコンバートする訳ではないので修正作業は手動で行う必要があります。

***Maven***

```xml
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-properties-migrator</artifactId>
</dependency>
```

***Gradle***

```groovy
runtime("org.springframework.boot:spring-boot-properties-migrator")
```

**例**

次のような古いフォーマットの記述があった場合

```properties:古いフォーマット
banner.charset: UTF-8 # Banner file encoding.
```

アプリケーション起動時にワーニングを表示してくれます。

```text
WARN 7460 --- [  restartedMain] o.s.b.c.p.m.PropertiesMigrationListener  : 
The use of configuration keys that have been renamed was found in the environment:

Property source 'applicationConfig: [classpath:/application.properties]':
	Key: banner.charset
		Line: 2
		Replacement: spring.banner.charset


Each configuration key has been temporarily mapped to its replacement for your convenience. To silence this warning, please update your configuration to use the new keys.
```

#### Hibernateプロパティのconfiguration

hibernateのプロパティをカスタマイズできます。

**サンプルコード**

```kotlin
@Bean
fun customizer() = HibernatePropertiesCustomizer { props ->
    props["hibernate.show_sql"] = true
    props["hibernate.format_sql"] = true
    props["hibernate.use_sql_comments"] = true
    props["hibernate.generate_statistics"] = true
}
```

application.propertiesでは次のプロパティでカスタマイズできます。

```text
spring.jpa.properties.hibernate.*
```

* [User Guild / configurations]
 (https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#configurations)

#### Redisのconfiguration

RedisCacheConfigurationでRedisCacheManagerの設定ができるようになっています。

```kotlin
@Bean
fun redisCacheConfig() = RedisCacheConfiguration.defaultCacheConfig()
    .entryTtl(Duration.ofMinutes(2))
    .disableCachingNullValues()
```

application.propertiesでは次のプロパティでカスタマイズできます。

```text
spring.cache.redis.*
```

#### Support

* [GSon] (https://github.com/google/gson)
* HTTP/2 (Jetty)



### [Spring Boot 2.0.0 RC2 Release Notes] (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0.0-RC2-Release-Notes)

* [Spring Boot 2.0.0 RC2 Configuration Changelog] (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0.0-RC2-Configuration-Changelog)

#### application.propertiesの変更

次のプロパティが再配置されています。(RC2で見直しされたようです)

**ENDPOINTS WEB CONFIGURATION**

```text
management.endpoints.web.expose
```

　　　↓

```text
management.endpoints.web.exposure.include
management.endpoints.web.exposure.exclude
```

**ENDPOINTS JMX CONFIGURATION**

```text
management.endpoints.jmx.expose
```

　　　↓

```text
management.endpoints.jmx.exposure.include
management.endpoints.jmx.exposure.exclude
```

#### Webjars Locatorの依存管理の変更

webjars-locatorの依存関係管理がwebjars-locator-coreに変更されています。

2.0より

***Maven***

```xml
<dependency>
    <groupId>org.webjars</groupId>
    <artifactId>webjars-locator-core</artifactId>
    <version>0.35</version>
</dependency>
```

***Gradle***

```groovy
compile("org.webjars:webjars-locator-core:0.35")
```

#### Kotlin

Spring Bootリファレンスのセクション47にKotlin supportが追加されています。

* [Part IV. Spring Boot features | 47. Kotlin support] (https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/reference/html/boot-features-kotlin.html)

また、Spring FrameworkのリファレンスにもKotlinのセクションがあります。

* [Spring Framework | Language Support] (https://docs.spring.io/spring/docs/5.0.4.RELEASE/spring-framework-reference/languages.html#kotlin)

#### Custom binding delimiter

以前からプロパティのタイプがコレクションで、バインドする値（例えばapplication.propertiesで定義する）がカンマ区切りのとき、自動的にコレクションへバインドしてくれていました。

```text
my-app.my-module.foo.hobbies= shopping,trekking,movies
```

```kotlin
@Component
@ConfigurationProperties(prefix = "my-app.my-module.foo")
data class FooConfig(
    var hobbies: List<String> = mutableListOf()
)
```

2.0よりDelimiterアノテーションを使って区切り文字を指定することができるようになっています。

```text
my-app.my-module.foo.hobbies= shopping; trekking; movies
```

```kotlin
@field:Delimiter(value = ";")
var hobbies: List<String> = mutableListOf()
```

逆にカンマ区切りで分割したくない場合は次のように指定します。

```kotlin
@field:Delimiter(value = Delimiter.NONE)
```

#### Support

* [Micrometer 1.0-GA] (http://micrometer.io/)
    * M5では1.0.0-RC2でしたが、GAをサポートします。

#### JOOQのSQLDialectの検出

デフォルトで自動検出が有効なので、通常はapplication.propertiesで明示する必要はありません。

```text
spring.jooq.sql-dialect= # SQL dialect to use. Auto-detected by default.
```

* [org.jooq.SQLDialect] ( http://www.jooq.org/javadoc/3.7.3/org/jooq/SQLDialect.html)

# リリースノートの補足

[Spring Boot 2.0 Migration Guide] (https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Migration-Guide)には、基本的にM1からRC2の内容がまとめられていますが、Migration Guideにだけ記載されている事項がありましたので補足します。

## spring.jpa.hibernate.ddl-autoのデフォルトの挙動の変更

1.5までのデフォルトは、組み込みデータベース使用時に`create-drop`、それ以外は`none`

2.0以降のデフォルトは、組み込みデータベース使用時で且つflyway/Liquibaseを使用していない時に`create-drop`、それ以外は`none`

このプロパティに設定できる値のその内容については変更はないようです。

* create-drop
    * Create and then destroy the schema at the end of session
* create
    * Create the schema and destroy previous data
* none
    * Disable DDL handling
* update
    * Update the schema if necessary
* validate
    * Validate the schema, make no changes to the database


# Spring Dataのバージョンアップの影響

* [Spring Data JPA - Reference Documentation - Version 1.11.10.RELEASE] (https://docs.spring.io/spring-data/jpa/docs/1.11.10.RELEASE/reference/html/)
* [Spring Data JPA - Reference Documentation - Version 2.0.4.RELEASE] (https://docs.spring.io/spring-data/jpa/docs/2.0.4.RELEASE/reference/html/)

Spring Data CommonsおよびJpaもバージョンアップしています。
このバージョンアップによりインターフェースの変更が行われているため実装コードの修正が必要な場合があります。

例えばCrudRepositoryインターフェースについては、次の違いがあります。

```java:jpa_1.11.10
@NoRepositoryBean
public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID>
```

```java:jpa_2.0.4
@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID>
```

|signature|JPA 1.11.10  |JPA 2.0.4 |
|:----|:----------|:-----------|
|同じ |\<S extends T\> S save(S entity) |\<S extends T\> S save(S entity) |
|違う |\<S extends T\> Iterable\<S\> save(Iterable\<S\> entities) |\<S extends T\> Iterable\<S\> saveAll(Iterable\<S\> entities) |
|違う |T findOne(ID id) |Optional\<T\> findById(ID id) |
|違う |boolean exists(ID id) |boolean existsById(ID id) |
|同じ |Iterable\<T\> findAll() |Iterable\<T\> findAll() |
|違う |Iterable\<T\> findAll(Iterable\<ID\> ids) |Iterable\<T\> findAllById(Iterable\<ID\> ids) |
|同じ |long count() |long count() |
|違う |void delete(ID id) |void deleteById(ID id) |
|同じ |void delete(T entity) |void delete(T entity) |
|違う |void delete(Iterable\<? extends T\> entities) |void deleteAll(Iterable\<? extends T\> entities) |
|同じ |void deleteAll() |void deleteAll() |