3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Kotlin + Spring boot2 + Webflux + ThymeleafをRouterFunctions使って実装する

Last updated at Posted at 2018-03-24

はじめに

家での開発環境でJavaでやってた箇所をKotlinに変えていこうとしています。
その過程でタイトルにあるような環境をとりあえず作れるようにと作ってみたメモです。

環境

IntelliJ IDEA
Kotlin 1.2.20
Java 8
Spring Boot 2.0.0.RELEASE

プロジェクト準備

今回は楽をしてInitializerを使用します。
IntelliJからSpringプロジェクトの生成は有料版じゃないと作れないらしいので、Web上から作成することにしています。
Dependenciesは後で追加すればいいので、空でもいいですし入れてもいいです。

build.gradle

spring-boot-starter
spring-boot-starter-webflux
spring-boot-starter-thymeleaf

は最低限必要となります。
コード作成する際はthymeleafがなくてもどうにかなってしまいますが、動かす時にエラーが出る上になぜエラーになったかさっぱり分からないので気を付けた方が良いです(3時間消費)。

Initializerで作成したものに追加するだけですが、一応動いた時の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'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.tasogarei'
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()
}


dependencies {
    compile('org.springframework.boot:spring-boot-starter')
    compile('org.springframework.boot:spring-boot-starter-webflux')
    compile('org.springframework.boot:spring-boot-starter-thymeleaf')
	compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
	compile("org.jetbrains.kotlin:kotlin-reflect")
	testCompile('org.springframework.boot:spring-boot-starter-test')
}

applicationクラス

それでは実装に入ります。
まずはBootを動かすメインクラスです。

今回の例ではなんとなくREACTIVEを指定してみたかったのでSpringApplicationBuilderで作成していますが、SpringApplication.run(Application::class.java, *args)でも動きます。

package com.tasogarei.application

import org.springframework.boot.WebApplicationType
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.builder.SpringApplicationBuilder

@SpringBootApplication
class Application

fun main(args: Array<String>) {
    SpringApplicationBuilder()
            .sources(Application::class.java)
            .web(WebApplicationType.REACTIVE)
            .run(*args)
}

Handlerクラス

リクエストをさばくHandlerを作成します。
今回はHTMLを返したいのでContentTypeにHTMLとrenderを使ってViewを返します。
Viewの位置はWebFlux使わない時と同じルールで記載します。

attributeはMap作ってrenderの引数に入れてあげれば良いですが、今回はお試しなので省略します。

package com.tasogarei.application.handler

import org.springframework.http.MediaType
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import reactor.core.publisher.Mono

@Component
class Handler {
    fun getIndex(req: ServerRequest): Mono<ServerResponse> {
        return ServerResponse.ok().contentType(MediaType.TEXT_HTML).render("index")
    }
}

RouterFunctionの作成

最後にRouterFunctionを作成します。
RouterFunctionを作成することによりMappingが集まるので、個人的にはControllerでMappingしていくより好みです。

なんとなくacceptとかnestとか使ってURLの構造が複雑になった時の対応をさせていますが、GET("/", handler::getIndex)これだけ書けば動きます。
まぁ、普通のWebアプリケーションであればすぐに増えるので最初から追加が容易なよういに書いておいた方が良いとは思ってます。

あと、使用するHandlerを渡していますが、routeIndex(handler: Handler)のようにFunction側にも渡せます。
Handlerが増えた時は下記の例のようにコンストラクタに書くのではなくFunctionを増やすべきなのかなと思いましたが、どちらが良いのかは分かってません。

package com.tasogarei.application.route

import com.tasogarei.application.handler.Handler
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.MediaType
import org.springframework.web.reactive.function.server.router

@Configuration
class Route(private val handler: Handler) {
    @Bean
    fun routeIndex() = router {
        accept(MediaType.TEXT_HTML).nest {
            GET("/", handler::getIndex)
        }
    }
}

動作確認

適当なHTMLを置いて起動してブラウザアクセスすれば見れるようになっているかと思います。

最後に

Dependency間違っててはまってしまいましたが、簡単にWebfluxでHTMLを扱えるようになりました。
Webflux使う使わないでメリットデメリットあるとは思いますが、好み的には使った方が好みなのでしばらく使っていこうかと思います。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?