Spring Boot2.0 KotlinでSpring Data Redisを使ってみたけど、けど

Kotlinはじめましたが、微妙なところではまります。
動きはしたのですが謎なところが多かったです。

とりあえず、軽く説明をします。
Apiを叩くとredisに保存、読み取りするだけのapiを作ります。

ソースはこちら
[GitHub spring boot 2.0](https://github.com/va034600/sample-kotlin/tree/master/sample-spring-boot-redis2)
[GitHub spring boot 1.5](https://github.com/va034600/sample-kotlin/tree/master/sample-spring-boot-redis2-for1_5)

# 実装
## gradle

```gradle:build.gradle
buildscript {
	ext {
		kotlinVersion = '1.2.20'
		springBootVersion = '2.0.0.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}")
	}
}

apply plugin: 'kotlin'
apply plugin: 'kotlin-spring'
apply plugin: 'eclipse-wtp'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'war'

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
compileKotlin {
	kotlinOptions {
		freeCompilerArgs = ["-Xjsr305=strict"]
		jvmTarget = "1.8"
	}
}
compileTestKotlin {
	kotlinOptions {
		freeCompilerArgs = ["-Xjsr305=strict"]
		jvmTarget = "1.8"
	}
}

repositories {
	mavenCentral()
}

configurations {
	providedRuntime
}

dependencies {
	compile('org.springframework.boot:spring-boot-starter-web')
	compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
	compile("org.jetbrains.kotlin:kotlin-reflect")
	providedRuntime('org.springframework.boot:spring-boot-starter-tomcat')
	testCompile('org.springframework.boot:spring-boot-starter-test')

	compile('org.springframework.boot:spring-boot-starter-data-redis')
	compile("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.4")
	compile("org.apache.commons:commons-pool2:2.5.0")
	compile("redis.clients:jedis:2.9.0")
}
```

１つ目の謎です。
spring-boot 1.5の時に追加したコンポーネントはspring-boot-starter-data-redis, jackson-module-kotlinだけでしたが、2.0の時はさらにcommons-pool2, jedisが必要でした。
spring-boot-starter-data-redisにcommons-pool2, jedisが依存していてもよさそうです。。。

## Kotlin

```kotlin:SampleController.kt
package com.example

import com.example.service.SampleService
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestMethod
import org.springframework.web.bind.annotation.RestController

@RestController
class SampleController(
        val sampleService: SampleService
) {
    @RequestMapping(value = ["/sample-redis"], method = [RequestMethod.GET])
    fun sampleReids(): SampleDto? {
        return sampleService.getSampleDto();
    }

    @RequestMapping(value = ["/sample-redis2"], method = [RequestMethod.GET])
    fun sampleReids2(): SampleDto? {
        return sampleService.getSampleDto2();
    }
}
```

```kotlin:SampleDto.kt
package com.example

import java.io.Serializable

class SampleDto(val id:Int, val name:String):Serializable
```

```kotlin:SampleConfiguration.kt
package com.example

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer
import org.springframework.data.redis.serializer.StringRedisSerializer

@Configuration
class SampleConfiguration {
    @Bean
    fun jedisConnectionFactory(): JedisConnectionFactory {
        return JedisConnectionFactory()
    }

    @Bean
    fun redisTemplate(): RedisTemplate<String, SampleDto> {
        val template  = RedisTemplate<String, SampleDto>()
        template.setConnectionFactory(jedisConnectionFactory())
        template.keySerializer = StringRedisSerializer()

        val jackson2JsonRedisSerializer = Jackson2JsonRedisSerializer(SampleDto::class.java)
        val om = jacksonObjectMapper()
        jackson2JsonRedisSerializer.setObjectMapper(om)
        template.valueSerializer = jackson2JsonRedisSerializer

        return template;
    }

    @Bean
    fun redisTemplate2(): RedisTemplate<String, String> {
        val template  = RedisTemplate<String, String>()
        template.setConnectionFactory(jedisConnectionFactory())
        template.keySerializer = StringRedisSerializer()
        template.valueSerializer = StringRedisSerializer()
        return template;
    }
}
```

２つ目の謎です。
connectionFactoryを設定する時の実装が1.5と2.0で違っていました。

```Kotlin:spring-boot 1.5
        template.connectionFactory = jedisConnectionFactory()
```

```Kotlin:spring-boot 2.0
        template.setConnectionFactory(jedisConnectionFactory())
```

本当は = で設定できるのですかね？


KotlinではJavaの時とは違い Jackson2JsonRedisSerializerを直接設定しても使えなかったので、作成した後にjacksonObjectMapperを設定しました。

redisTemplateの型パラメータを使ってSampleDtoをAnyにしたかったのですが、うまくいきませんでした。
たぶんできるとおもうんだけど。。。
なので、ServiceでredisTemplate2でjson変換をしてConfigurationで文字列を保存するようにしました。

```kotlin:SampleService.kt
package com.example.service

import com.example.SampleDto

interface SampleService {
    fun getSampleDto(): SampleDto?
    fun getSampleDto2(): SampleDto?
}
```

```kotlin:SampleServiceImpl.kt
package com.example.service

import com.example.SampleDto
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.stereotype.Service

@Service
class SampleServiceImpl(
        val redisTemplate:RedisTemplate<String, SampleDto>,
        val redisTemplate2:RedisTemplate<String, String>
):SampleService {
    override fun getSampleDto():SampleDto? {
        redisTemplate.opsForValue().set("test_dto", SampleDto(1, "aab"))
        return redisTemplate.opsForValue().get("test_dto")
    }

    override fun getSampleDto2():SampleDto? {
        val sampleDto = SampleDto(1, "aac")

        val om = jacksonObjectMapper()

        var jsonStr = om.writeValueAsString(sampleDto)
        redisTemplate2.opsForValue().set("test_dto2", jsonStr)

        val jsonStr2 = redisTemplate2.opsForValue().get("test_dto2")
        return om.readValue(jsonStr2 ?: "{}", SampleDto::class.java)
    }
}
```

# 参考
- [Jackson Kotlin Module Example](https://gist.github.com/hansenji/6bd58f6d217d9ffe78159fef75b8bf7e)
- [Kotlin – Convert Object to/from JSON with Jackson 2.x](http://kotlination.com/kotlin/kotlin-convert-object-to-from-json-jackson-2)
- [Java: Spring Boot で Redis を使う！
](http://blog.rakugakibox.net/entry/2015/07/27/spring-boot-with-redis)

Kotlin、難しいです。。。
謎だらけです。

[GitHub spring boot 2.0](https://github.com/va034600/sample-kotlin/tree/master/sample-spring-boot-redis2)
[GitHub spring boot 1.5](https://github.com/va034600/sample-kotlin/tree/master/sample-spring-boot-redis2-for1_5)