Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
SpringBootとKtorを雑に比較してみる
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
chiharu terashima
March 25, 2020
Programming
1k
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
SpringBootとKtorを雑に比較してみる
chiharu terashima
March 25, 2020
More Decks by chiharu terashima
See All by chiharu terashima
アイテムレビュー基盤で導入したアーキテクチャとその成果 / Item Review Introduction Architecture Outcome
chichi1091
1
3.7k
Other Decks in Programming
See All in Programming
PyO3 で既存 Python 評価器を Rust core 化する ー wasm-bindgen でブラウザにも配るための設計
kdash
1
420
go-spidermonkeyでAIエージェントのCode Modeを実装する
syumai
3
1.5k
Press start. Python's next generation.
willingc
PRO
3
310
Vibes Containers 〜AIで変わるコンテナ設計と運用〜
tkikuc
1
490
[DroidKaigi 2026] Bring your own phones to Gradle Managed Devices
f2lk
0
110
新人はどこまで自力でやり、どこからAIに頼るべきか/エンジニア育成に向き合う_先輩たちの悩みと知見共有会
toppan_digital_dev
1
560
新卒PdEのリアル
ryu1013
1
410
Pythonの実行はどこまで賢くなったのか? CPythonとPyPyから見る最適化のしくみ
curekoshimizu
4
2.8k
Building an Out-of-Order CPU
latte72
0
680
XHTMLが残したもの
yosuke_furukawa
PRO
1
410
PyConJP2026_wat_Python × Signal Processing: How to Draw Pictures with Sound Using Spectrogram Art
wat
0
670
MIZARU@SPAJAM2026 第二回予選
1901drama
0
110
Featured
See All Featured
How to Ace a Technical Interview
jacobian
281
24k
Unsuck your backbone
ammeep
672
58k
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
230
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
440
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
3k
Google's AI Overviews - The New Search
badams
0
1.6k
The Anti-SEO Checklist Checklist. Pubcon Cyber Week
ryanjones
0
230
From π to Pie charts
rasagy
0
350
Tell your own story through comics
letsgokoyo
1
1.1k
Tips & Tricks on How to Get Your First Job In Tech
honzajavorek
1
720
How to Align SEO within the Product Triangle To Get Buy-In & Support - #RIMC
aleyda
2
1.8k
Why Our Code Smells
bkeepers
PRO
340
58k
Transcript
SpringBootͱKtorΛ ࡶʹൺֱͯ͠ΈΔ ͳ͕ͷJava #1 2020.03.25 @chichi1091
SpringBootͱKtorΛ ࡶʹൺֱͯ͠ΈΔ ͳ͕ͷJava #1 2020.03.25 @chichi1091
ࣗݾհ • ࣉౢઍʗͯͬ͠ʔʢ@chichi1091ʣ • גࣜձࣾHolmes • αʔόαΠυΤϯδχΞ • SpringBoot(Java,Kotlin),Groovy https://chichi1091.hatenablog.jp/
https://github.com/chichi1091
© Holmes,inc. All right reserved. ܖϚωδϝϯτγεςϜ ʮϗʔϜζΫϥυʯ ܖʹɺ Ϛωδϝϯτͷ ࢹΛɻ
© Holmes,inc. All right reserved. ։ൃڥ
։ൃʹؔΘΔશ৬छੵۃ࠾༻தͰ͢ʂ – ϓϩμΫτϚωʔδϟ – UI/UXσβΠφʔ – webϑϩϯτΤϯυΤϯδχΞ – αʔόαΠυΤϯδχΞ –
SRE
Ktorͱʁ
Ktorͱʁ • KotlinΛ։ൃ͍ͯ͠ΔJetBrains͕ࣾ։ൃͨܰ͠ྔ WebϑϨʔϜϫʔΫ • featureΛՃ͢Δ͜ͱͰ֦ுػೳͷར༻͕Մೳ • ςϯϓϨʔτΤϯδϯ • ೝূػೳ
• DI
SpringBootͱൺֱ
SpringBootͱൺֱ • ීஈ͍ͬͯΔSpringBootͱൺֱ͢Δ͜ͱͰཧղ ΛਂΊ͔ͨͬͨ • 100%KotlinͷϑϨʔϜϫʔΫʹڵຯ͕͋ͬͨ • ӡ༻ʹ͑ΒΕΔ͔ௐ͔ͨͬͨ
ΈࠐΈAPαʔό
ΈࠐΈAPαʔόʢSpringBootʣ • Buildπʔϧʹspring-boot-starter-xxxΛΈࠐΉ ͜ͱͰΓସ͕͑Մೳ • tomcat • jetty • undertow
ΈࠐΈAPαʔόʢKtorʣ • Buildπʔϧʹktor-server-xxxΛΈࠐΉ͜ͱͰ Γସ͕͑Մೳ • Netty • jetty • tomcat
BootRun
BootRun @SpringBootApplication @Controller class Application { @GetMapping("/") fun index(): String
= "Hello, Kotlin" } fun main(args: Array<String>) { runApplication<BlogApplication>(*args) } class Application fun main(args: Array<String>) { val server = embeddedServer(Netty, 8080) { routing { get("/") { call.respond(HttpStatusCode.OK,"Hello, Kotlin") } } } server.start() } 4QSJOH#PPU ,UPS
ςϯϓϨʔτΤϯδϯ
ςϯϓϨʔτΤϯδϯ • SpringBoot • Thymeleaf • Freemarker • Groovy Template
• Ktor • Thymeleaf • Freemarker • Velocity • Mustache • Kotlin DSLͰHTMLCSSΛॻ͘͜ͱՄೳ
ςϯϓϨʔτΤϯδϯʢKotlin DSLʣ get("/styles.css") { call.respondCss { body { backgroundColor =
Color.red } p { fontSize = 2.em } rule("p.myclass") { color = Color.blue } } } suspend inline fun ApplicationCall.respondCss(builder: CSSBuilder.() -> Unit) { this.respondText(CSSBuilder().apply(builder).toString(), ContentType.Text.CSS) } w ,PUMJOίʔυͰ$44Λੜɾग़ྗ
ςϯϓϨʔτΤϯδϯʢKotlin DSLʣ get("/") { call.respondHtml { head { link(rel =
"stylesheet", href = "/styles.css", type = "text/css") } body{ h1 { id = "title" +"ͳ͕ͷJava" } p("myclass") { +"myclass" } ul { for(n in 1..10) { li { onClick = "alert($n)" +"$n" } } } } } } w ,PUMJOίʔυͰ)5.-Λੜɾग़ྗ
Controller
ControllerʢSpringBootʣ @Controller //@RestController @RequestMapping("/users") class UsersApplication{ @GetMapping("/") fun list(): String
= "users routing ok" @GetMapping("/{id}") fun index(@PathVariable(“id") id: String): String = "user $id routing ok" }
ControllerʢKtorʣ fun main(args: Array<String>) { val server = embeddedServer(Netty, 8080)
{ routing { userController() } } server.start() } fun Route.userController() { route("/users") { get() { call.respondText { "users routing ok" } } get("/{id}") { val id = call.parameters["id"] call.respond(HttpStatusCode.OK, "user $id routing ok") } } } w SPVUFͰΤϯυϙΠϯτΛࢦఆ w ͦͷதʹϧʔςΟϯάઃఆ͠ΞΫγϣ ϯΛࢦఆ͢Δ
ྫ֎ϋϯυϥʔ
ControllerʢSpringBootʣ class ExceptionHandler: ResponseEntityExceptionHandler() { @ExceptionHandler(Exception::class) fun handleException(ex: Exception, headers:
HttpHeaders, request: WebRequest): ResponseEntity<Any> { return super.handleExceptionInternal(ex, "handleException", headers , HttpStatus.INTERNAL_SERVER_ERROR, request) } }
ControllerʢKtorʣ fun main() { val server = embeddedServer(Netty, port =
8082) { install(StatusPages) { exception<XxxException>{ cause -> call.respond(HttpStatusCode.InternalServerError) } exception<AuthorizationException>{ cause -> call.respond(HttpStatusCode.Forbidden) } } routing { userController() } } server.start() } class XxxException: RuntimeException() class AuthorizationException: RuntimeException() w ྫ֎ʹΑͬͯฦ͢εςʔλείʔυΛ ఆٛ
Session
SessionʢSpringBootʣ @Controller //@RestController @RequestMapping("/users") class UsersApplication{ @GetMapping("/") fun list(request: HttpServletRequest):
String { val session = request.session session.setAttribute("key", "name") val name = session.getAttribute("key") return "users routing ok" } } w Լهͷྫ4FTTJPOΛར༻͢Δํ๏ w ଞʹ w !4FTTJPO"UUSJCVUFT w !4DPQF ηογϣϯείʔϓ
SessionʢKtorʣ fun main() { val server = embeddedServer(Netty, port =
8082) { install(Sessions) { cookie<MySession>("MY_SESSION") { cookie.extensions["SameSite"] = "lax" } } routing { get() { val session: MySession = call.sessions.get<MySession>() ?: MySession("1") call.sessions.set("MY_SESSION", session.copy("2")) call.respondText { "users routing ${session.id}" } } } } server.start() } data class MySession ( val id: String ) w ʮ.:@4&44*0/ʯ͕$PPLJF໊
DBଓ
DBଓʢSpringBootʣ @Entity data class Users ( @Id @GeneratedValue(strategy=GenerationType.IDENTITY) val id:
Long, val name: String, val birthday: Date ) @Repository interface UsersRepository: JpaRepository<Users, Long> {} @RestController class UsersController( private val usersRepository: UsersRepository ) { @GetMapping(value = ["/"]) @ResponseStatus(HttpStatus.OK) fun index(): List<Users> = usersRepository.findAll() } • ORϚούʔJPA • τϥϯβΫγϣϯAOPͰ੍ޚ
DBଓʢKtorʣ • ORϚούʔExposed • ͪΖΜKotlin • JetBrains͕ࣾ։ൃ • BootRun࣌ʹH2ଓ fun
main(args: Array<String>) { // DBଓ Database.connect("jdbc:h2:mem:ktor_db;DB_CLOSE_DELAY=-1", "org.h2.Driver") val server = embeddedServer(Netty, 8080) { routing { userController() } } server.start() }
DBଓʢKtorʣ // Dao object Users : LongIdTable() { val name:
Column<String> = varchar("name", 50) val birthday: Column<DateTime> = date("birthday") } // Entity class User(id: EntityID<Long>): LongEntity(id) { companion object : LongEntityClass<User>(Users) var name by Users.name var birthday by Users.birthday } fun Route.userController() { val service by inject<UserService>() route("/users") { post() { SchemaUtils.create(Users) // ϚΠάϨʔγϣϯ val cal = Calendar.getInstance() cal.set(Calendar.YEAR, 2020) cal.set(Calendar.MONTH, 3 -1) cal.set(Calendar.DAY_OF_MONTH, 25) val today = DateTime(Calendar.getInstance().timeInMillis) transaction { User.new { name = "kotlin ktor" birthday = DateTime(today) } } call.respondText { "users insert" } } put() { transaction { Users.update ({ Users.id eq 1 }) { it[Users.name] = "kotlin OExposed" } } call.respondText { "users update" } } delete() { transaction { Users.deleteWhere { Users.id eq 1 } } call.respondText { "users delete" } } get() { var result: List<User> = listOf() transaction { result = User.all().sortedByDescending{ it.birthday } } call.respondText { "users routing ${service.getName()}" } } } } w 42-ൃߦ࣌USBOTBDUJPOඞਢ w ݕࡧଞʹ val user: SizedIterable<User> = User.find { Users.id eq 1 } val user: User? = User.findById(1L)
JSON
JSONʢSpringBootʣ @RestController @RequestMapping("/users") class UsersApplication{ @GetMapping("/") fun list(request: HttpServletRequest): ResponseEntity<Json>
{ val json = Json(1,"Kotlin") return ResponseEntity.of(Optional.of(json)) } } data class Json( private val id: Int, private val name: String )
JSONʢKtorʣ fun main() { val server = embeddedServer(Netty, port =
8082) { install(ContentNegotiation) { jackson { configure(SerializationFeature.INDENT_OUTPUT, true) } } routing { get() { call.respond(Json(1, "Ktor")) } } } server.start() } data class Json( private val id: Int, private val name: String ) w JOTUBMMͰ+BDLTPOͷઃఆ w ฦ٫͞ΕΔ+40/Λܗ
DI
DIʢSpringBootʣ @Service class UserService { fun getName(): String { return
"Kotlin" } } @RestController class UserController( private val userService: UserService ) { @GetMapping("/") fun index(): String { return "Hello, ${userService.getName()}" } }
DIʢKtorʣ • KtorͰDI͢ΔʹʮKodeinʯ͔ʮKoinʯΛ͏ • ࠓճKoin fun main() { val server
= embeddedServer(Netty, port = 8082) { install(Koin) { modules(module) } routing { userController() } } server.start() } val module = module(createdAtStart = true) { singleBy<UserService, UserService>() } class UserService { fun getName(): String{ return "Kotlin" } } fun Route.userController() { val service by inject<UserService>() route("/users") { get() { call.respondText { "users routing ${service.getName()}" } } } } w JOTUBMM ,PJO Ͱ%*ίϯςφొ w γϯάϧτϯͰ6TFS4FSWJDFΛొ w JOKFDUͰऔಘ
·ͱΊ
·ͱΊ • SpringBootͱಉͷfeature͕ἧ͍ͬͯΔΑ͏ʹ ײͨ͡ • υΩϡϝϯτΘ͔Γ͍͢(https://ktor.io/) • Githubͷߋ৽׆ൃ • featureͲΜͲΜ૿͑ͦ͏ʁ
• SpringBootΑΓKotlinͬΆ͍ίʔυ͕ॻ͚Δ • ຊ൪ར༻͑ΒΕͦ͏