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
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
chiharu terashima
March 25, 2020
Programming
990
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.6k
Other Decks in Programming
See All in Programming
dRuby over BLE
makicamel
2
330
IBM Bobを活用したレガシーアプリの最新化
oniak3ibm
PRO
1
180
Technical Debt: Understanding it Rightly, Engaging it Rightly #LaravelLiveJP
shogogg
0
210
tsserverとは何だったのか、これからどうなるのか
nowaki28
1
460
キャリア迷子上等 ─ "ない道"は自分で作ればいい
16bitidol
3
1.9k
Oxcを導入して開発体験が向上した話
yug1224
4
300
These Five Tricks Can Make Your Apps Greener, Cheaper, & Nicer
hollycummins
0
280
AIとASP.NET Coreで雑Webアプリを作った話
mayuki
0
460
エージェンティックRAGにAWSで入門しよう!
har1101
8
1.3k
ECSアプリログをFireLensでコスト削減しようとしたけど諦めた話 in Fargate×Node.js
akihisaikeda
2
3.9k
並列実装の現場、2ヶ月間実務でAIを使い倒したAIもPCも私も限界が近い
ming_ayami
0
110
生成AI時代にこそ効くGo | Why Go Works in the Age of Generative AI
mom0tomo
8
3.2k
Featured
See All Featured
From π to Pie charts
rasagy
0
200
Bootstrapping a Software Product
garrettdimon
PRO
307
120k
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
Everyday Curiosity
cassininazir
0
230
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
600
Taking LLMs out of the black box: A practical guide to human-in-the-loop distillation
inesmontani
PRO
3
2.3k
So, you think you're a good person
axbom
PRO
2
2.1k
エンジニアに許された特別な時間の終わり
watany
107
250k
AI: The stuff that nobody shows you
jnunemaker
PRO
8
700
How to optimise 3,500 product descriptions for ecommerce in one day using ChatGPT
katarinadahlin
PRO
1
3.6k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
HDC tutorial
michielstock
2
700
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ͬΆ͍ίʔυ͕ॻ͚Δ • ຊ൪ར༻͑ΒΕͦ͏