開篇扯犢子:
最近在學kotlin,有些懵啊,學了基本也忘了點,工作很忙最近才好點,這句話是今天加上的,寫這個部落格寫了好幾天,一天隻能寫一點,沒時間啊,我不是那種可以睡很少覺的人,我要是熬夜就不能保證第二天的工作品質了,煩啊。而且無奈業餘時間又有限,真是很沖突啊很尴尬啊,為了和大學同學住一個房子,每天上班要一個半小時,下班一個半小時。還好我女朋友送了我一個kindle(秀一下),可以在路上學習,不然我要瘋啊。
不禁想起劉帥微信群裡的一些大佬們,天天都在學習,一問他們在幹嗎,都在說公司最近閑得很,要跳槽之類的。。。。。。
他們怎麼那麼閑啊,有一位大佬,天天去打遊戲,遊戲打膩了,就去學學習,md這幾天他又宣布了,公司成為了國企,o( ̄︶ ̄)o我說怎麼那麼閑呢。
kotlin可以寫Android,IOS,也可以開發後端,利用kotlin成為一個全棧工程師,是十分簡單的。于是就試着用kotlin搭建了一下背景服務,能搭起來,還不錯哈哈哈哈。
正文:kotlin開發後端的優點:
- 快,建構起來是挺快的。也可以配置一下kotlin的熱部署AutoReload,使項目在開發的時候再尼瑪快點。
- 一些其他特性:看官網自己吹就行了,比我吹得要好一些。
- 可以拿來裝13。
一、保證jdk和idea都安好了。
二、利用ktor插件建立ktor項目:
idea安裝ktor插件:
安裝完了之後,開始利用插件建立項目:
File->new Project 就會看到這個畫面:
1:你的jdk。
2:選擇ktor插件。
3:選擇你需要的一些特性。在這裡不選擇也行,到項目中可以利用gradle配置,等以後學到這再說這些都是幹啥的。
4:同3。
點選next之後一路next+finish。
三、建立項目完畢。
如果建立項目時沒有選擇3處的一些架構,建立項目完畢後,可以在gradle中添加,例如添加netty和logback:
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
compile "io.ktor:ktor-server-netty:$ktor_version"
compile "ch.qos.logback:logback-classic:1.2.1"
}
建立項目完畢後,看到如圖所示結構:widget是我自己寫的,忽略掉即可,正常建立之後沒有。
application.conf檔案:顧名思義,是一個配置檔案,配置的modules既是application.kt中的module,如下:
如果建立的包含main的kt檔案命名不是application.kt也沒關系,隻要和application.conf中的modules後面的名字一樣即可。
在idea中,我們建立好的項目,會自動識别包含main方法的配置,可以直接運作起來。有的時候這個配置會報紅,通常是module找不到的原因。我們如下圖所示把module配置上即可。
1. 點選配置
2. 點選倒三角找到要運作的module
3. 點選他。配置完畢。
四、敲代碼:
一、路由:
如果gradle中沒有配置netty,那就配一下子:
compile "io.ktor:ktor-server-netty:$ktor_version"
compile "io.ktor:ktor-server-core:$ktor_version"
一些必不可少的,建立項目之後就會自己有的東西,檢查一下:
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
compile "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinx_coroutines_version"
compile "org.jetbrains.kotlinx:kotlinx-coroutines-io:$kotlinx_coroutines_version"
compile "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:$kotlinx_coroutines_version"
以上配置通過之後,路由這個東東賊雞兒簡單。
在我們的fun Application.module() {}中寫一個routing{},在routin中寫一個普通的get方法:
routing {
get("/") {
call.respondText("HELLO WORLD!Ktor", contentType = ContentType.Text.Plain)
}
}
然後浏覽器打開:http://0.0.0.0:8080/
(這裡注意,如果你是自己利用插件建立的項目,建立好之後main方法是這樣的:
fun main(args: Array<String>): Unit = io.ktor.server.netty.DevelopmentEngine.main(args)
此時應該打開localhost的8080端口,才能看到結果。
如果你是和我一樣,吧main改成了:
fun main(args: Array<String>) {
embeddedServer(Netty, , watchPaths = listOf("applicationKt"), module = Application::module).start()
}
那就打http://0.0.0.0:8080/。原因是embeddedServer中指定的host是0.0.0.0。
還可以在項目編譯的時候,列印的日志中,找到Responding at xxxx 。這個xxxx就是你要打開的連接配接。
)
打開後看到結果:
改一下這部分文字:
注意左下,改了代碼之後點選這個Rerun,不要點選項目右上角的run。不然則會告訴你端口已經被占用了哈哈哈。自己想想怎麼回事。
rerun後結果:
二、gson傳回json串:
好多教程都是用jackson。作為一個Android開發者,兩個公司都用的gson。我自然對jackson沒有好感。
1、配置gson:
配置這個gson有坑啊,這個gradle正确的連接配接找不到,不管用,後來在github的ktor官網上發現,應該是這個連接配接:
對應的github中的位址:
學習方法:所有ktor項目的特性,在ktor的github中都能找到。找到之後,看他的test怎麼寫的,你就怎麼寫。牛逼不?
2、gson傳回map對象和接收map對象:
代碼:還是在module裡面寫:
install(ContentNegotiation) {//引入我們需要的gson以及注冊gsonconverter
register(ContentType.Application.Json, GsonConverter())
}
routing {
val model = mapOf("id" to , "title" to "Hello, World!", "unicode" to uc)
get("/gson") {
call.respond(model)
}
post("/gson") {
val map = call.receive<Map<*, *>>()
val text = map.entries.joinToString { "${it.key}=${it.value}" }
call.respond(text)
}
}
這裡有個uc是:
經過base64編碼之後是個T。
寫好代碼之後rerun,浏覽器中輸入http://0.0.0.0:8080/gson
或者localhost的這個:8080/gson就會看到:
接收gson代碼也簡單,
call.receive<Map<*, *>>()
接受一個map對象。
然後
val text = map.entries.joinToString { "${it.key}=${it.value}" }
對其進行周遊并轉換為string。
然後
call.respond(text)
傳回結果。
3、gson傳回實體類對象。
定義實體類,kotlin就兩行代碼就搞定兩個類,牛逼不?:
data class MyEntity(val id: Int, val name: String, val children: List<ChildEntity>)
data class ChildEntity(val item: String, val quantity: Int)
傳回實體類:
routing {
val model = MyEntity(, "Cargo", listOf(ChildEntity("Qube", ), ChildEntity("Sphere", ), ChildEntity(uc, )))
get("/gsonEntity") {
call.respond(model)
}
post("/gsonEntity") {
val entity = call.receive<MyEntity>()
call.respond(entity.toString())
}
}
打開:http://0.0.0.0:8080/gsonEntity 檢視傳回結果:
三、freemarker寫html頁面:
freemarker就是用來。。。拷貝一下官方文檔:
Apache FreeMarker™是一個模闆引擎:一個Java庫,用于根據模闆和更改資料生成文本輸出(HTML網頁,電子郵件,配置檔案,源代碼等)。 模闆是用FreeMarker模闆語言(FTL)編寫的,這是一種簡單的專用語言(不像PHP這樣的完整程式設計語言)。 通常,使用通用程式設計語言(如Java)來準備資料(釋出資料庫查詢,進行業務計算)。 然後,Apache FreeMarker使用模闆顯示準備好的資料。 在模闆中,您将關注如何呈現資料,而在模闆之外,您将關注于要呈現的資料。
嗯,果然正經多了。
1、建構freemarker:
建立ftl檔案:
這個ftl檔案就和我們用過html一樣。idea裡支援ftl的格式,可以直接格式化代碼。
2、編寫頁面:
<#-- @ftlvariable name="data" type="com.example.IndexData" -->
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
hahaha
<ul>
<#list data.items as item>
<li>${item}</li>
</#list>
</ul>
</body>
</html>
3、傳回頁面
在module裡面寫:
install(FreeMarker) {//引入freemark并指定加載的檔案夾
templateLoader = ClassTemplateLoader(this::class.java.classLoader, "templates")
}
routing {//傳回index.ftl的内容,并
get("/freemarker") {
call.respond(FreeMarkerContent("index.ftl", mapOf("data" to IndexData(listOf(, , ))), ""))
}
}
data class IndexData(val items: List<Int>)
以上代碼就是,我們先引入freemarker架構,然後傳回index.ftl。并且傳個參數data。
打開:http://0.0.0.0:8080/freemarker 檢視結果:
開發html的時候,寫完html可以直接浏覽器預覽,很友善,那ftl檔案可以嗎?
ftl檔案也是可以寫完之後馬上看到結果的:
我們在ftl頁面右擊(mac輕按兩下),然後選擇Recompile,就行了。
freemarker内部是利用管道原理和buffwriter實作的:我又得複習了:
package io.ktor.freemarker
import freemarker.template.*
import io.ktor.application.*
import io.ktor.cio.*
import io.ktor.content.*
import io.ktor.http.*
import io.ktor.response.*
import io.ktor.util.*
import kotlinx.coroutines.experimental.io.*
class FreeMarkerContent(val template: String,
val model: Any?,
val etag: String? = null,
val contentType: ContentType = ContentType.Text.Html.withCharset(Charsets.UTF_8))
class FreeMarker(val config: Configuration) {
companion object Feature : ApplicationFeature<ApplicationCallPipeline, Configuration, FreeMarker> {
override val key = AttributeKey<FreeMarker>("freemarker")
override fun install(pipeline: ApplicationCallPipeline, configure: Configuration.() -> Unit): FreeMarker {
val config = Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS).apply(configure)
val feature = FreeMarker(config)
pipeline.sendPipeline.intercept(ApplicationSendPipeline.Transform) { value ->
if (value is FreeMarkerContent) {
val response = feature.process(value)
proceedWith(response)
}
}
return feature
}
}
private fun process(content: FreeMarkerContent): FreeMarkerOutgoingContent {
return FreeMarkerOutgoingContent(config.getTemplate(content.template), content.model, content.etag, content.contentType)
}
private class FreeMarkerOutgoingContent(val template: Template,
val model: Any?,
etag: String?,
override val contentType: ContentType) : OutgoingContent.WriteChannelContent() {
override suspend fun writeTo(channel: ByteWriteChannel) {
channel.bufferedWriter(contentType.charset() ?: Charsets.UTF_8).use {
template.process(model, it)
}
}
init {
if (etag != null)
versions += EntityTagVersion(etag)
}
}
}
四、html-dsl寫html頁面:
1、還是老樣子,配置gradle:其實這個可以在建構項目的時候,直接勾選的。
compile "io.ktor:ktor-html-builder:$ktor_version"
compile "org.jetbrains:kotlin-css-jvm:1.0.0-pre.31-kotlin-1.2.41"
2、敲代碼,還是那個module:寫個 routing {}
get("/html-dsl") {
call.respondHtml {
head { link(rel = "stylesheet", href = "/styles.css") }
body {
h1 { +"HTML" }
ul {
for (n in ) {
li { +"$n" }
}
}
p { +"唉呀媽呀" }
}
}
}
get("/styles.css") {
call.respondCss {
body {
backgroundColor = Color("#aaccff")
}
p {
fontSize = em
fontStyle = FontStyle("italic")
}
rule("p.myclass") {
color = Color.blue
}
}
}
以上代碼就是i寫一個html并且連接配接自己寫的css。傳回css方法:
suspend inline fun ApplicationCall.respondCss(builder: CSSBuilder.() -> Unit) {
this.respondText(CSSBuilder().apply(builder).toString(), ContentType.Text.CSS)
}
檢視結果:
dsl的好處就是一旦運作起來之後,效率比html快。但是再快還能快到哪裡去?我對官方給出的這個說法提出鄙視。
而且每次修改改完都要Rerun。還好官方給出了可以配置項目快速編譯的方法,可以緩解一小下尴尬。
通常,重新啟動伺服器可能需要一些時間,是以Ktor提供了一個基本的自動重載工具,可以重新加載Application類。
ktor說的:
Autoreload在Java 9中不起作用。如果您想使用它,請立即堅持使用JDK 8。
使用自動重新加載時會有性能損失。 是以請記住,您不應該在生産中或在進行基準測試時使用它。
AutoReload的文檔:
https://ktor.kotlincn.net/servers/autoreload.html#java9
看不懂我再來個翻譯帖。明後天發。
五、下期預告:
- kotlin連接配接資料庫。
- 可能會有AutoReload。
- 會有小姐姐。