感想
Kotlin 是一門好語言,值得大家了解一下。
Vertx 是一個好框架,也值得大家了解一下。
Kotlin
寫過js,也寫過一點點go,主力一直是java。用了kotlin,貌似找到了常用語言的平衡點了。
Kotlin 擁有一些偏函數式的語法(java8 也引入了一些),提供了相當多便捷的api與一些高階函數。從兩天的試用,以及今天搞得這個 Vertx web 項目,從中體會到最爽的有兩點:
- 支持“帶接收者得函數字面值”(允許你直接指定函數的receiver的類型)這一特性。這個特性,在go里面經常看到。然而,java沒有,java8也沒有...
- 支持擴展函數(或許是我見識短,這功能爆炸了)
一直很期待可以指定receiver這個功能。有了這個特性,那么寫的函數,可以直接被調用者使用。
Vertx
vertx 風格和node的express框架思想一致的,換了一種java的實現。不得不說,node的express 啟發了很多其他語言的web框架設計。java的vertx,以及go里面的很多web框架(martin...),很多都有express的影子(難道是我先入為主?)
相比傳統的基於Servlet的java web框架,vertx這種基於封裝底層通信的框架,在速度上和內存占用上比較有優勢。曾經為了在768M內存的docker容器上跑web應用,先是用相對較輕量級的spring-boot,勉強可以跑。然后又用了Node 的 express,這個毫無壓力。
終於有一個java版本的這種web框架,整個項目打完包,包括依賴的 lib,整個才4-5M的大小(主要是lib大小),太輕量了。
下面就看看 Kotlin + Vertx 寫的web項目,展示下kotlin的魅力。你要是比較懶的話,想直接check out代碼,github庫在這里。
Kotlin & Vertx
1.maven 配置
按照kotlin和vertx官方的配置。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>vertx</groupId>
<artifactId>com.vertx</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>com.tt.vertx.HelloWorlds</Main-Class>
<Main-Verticle>com.tt.vertx.HelloWorlds</Main-Verticle>
</manifestEntries>
</transformer>
</transformers>
<artifactSet/>
<outputFile>${project.build.directory}/${project.artifactId}-${project.version}-fat.jar</outputFile>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<sourceDirs>
<source>src/main/java</source>
</sourceDirs>
</configuration>
</execution>
<execution>
<id>test-compile</id>
<phase>process-test-sources</phase>
<goals>
<goal>test-compile</goal>
</goals>
<configuration>
<sourceDirs></sourceDirs>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
<properties>
<kotlin.version>1.0.3</kotlin.version>
</properties>
</project>
2.程序入口
和spring-boot 一樣,vertx 的程序入口也是一個靜態的main函數。
class HelloWorlds : AbstractVerticle() {
companion object{
@JvmStatic fun main(args: Array<String>){
var vertx = Vertx.vertx()
vertx.deployVerticle(HelloWorlds())
}
}
override fun start() {
var router = customRouter(vertx)
println("server running on 8888")
vertx.createHttpServer().requestHandler({ handler -> router.accept(handler)}).listen(8888)
}
}
如果寫過nodejs應用,這段代碼看起來就很簡單了。繼承vertx的啟動類,在啟動的時候,設置路由,綁定http server的端口。
注意
和普通的kotlin運行類不一樣,入口主類需要打jar包的時候,主方法(main)一定是要是java的標准靜態方法. 需要加 @JvmStatic 標注一下。否則運行打出來的jar包,會找不到靜態主入口。
在這里,封裝了一下路由,提取到了單獨的文件中。
3.路由設置
fun customRouter(vertx : Vertx) : Router {
var router = Router.router(vertx)
router.route("/").handler({c -> c.response().html().end("hello world")})
router.route("/json").handler({c -> c.response().json().end(Json.encode(Entity("name","sss")))})
return router
}
fun HttpServerResponse.html() : HttpServerResponse {
return this.putHeader("content-type","text/html")
}
fun HttpServerResponse.json() : HttpServerResponse {
return this.putHeader("content-type","application/json; charset=utf-8")
}
讓我覺得kotlin拯救了我的地方就在這一段路由代碼里。
先看一下,之前用純java寫的路由版本是什么樣的吧,對比一下。
java版本:
router.route("/").handler(context -> context.response().putHeader("content-type","text/html").end("hello world"));
router.route("/json").handler(context -> {
context.response().putHeader("content-type","application/json; charset=utf-8")
.end(Json.encodePrettily(new Entity("hello","world")));
});
在java版本里面,給每個請求加header的時候,是要加 .putHeader("content-type","xxxx") 的。
試想一下,如果我們有上百個路由的話,每個頭里面都要指定這些header,多痛苦。根據經驗,我們可能想把它提出來,當做一個方法。於是我們需要寫一個工具類之類的,給context.response()的結果 HttpServerResponse 加上header,然后再返回這個 HttpServerResponse,最直觀的大概寫法應該是:
SomeUtil.setHeader(context.response()).end('xxx')
不管怎么說,因為java的語法限制,我們貌似只能這么寫(如果有更好的方法,請大家共享一下)。
看一下kotlin版本的,把加header的操作提了出來,弄成了兩個函數。這兩個函數是函數接收字面量和擴展函數的結合體,使用擴展函數,這是個,也必須聲明一個函數的接受者類型。
我們定義html() 和 json() 函數, 這兩個方法只能用HttpServerResponse 去執行它,並且返回執行后的該 HttpServerResponse。 在擴展函數里面,this指向的是該函數的調用者。語法官方有詳細文檔,就少說了(好多語法我也沒有看完 ORZ )。
這個特性超級好用,尤其是在這種鏈式的調用里面,我們不用在去另寫寫轉換方法,不用在把要轉換的對象當參數來回傳遞了。
4. 還有一個bean 對象
data class Entity(var name:String,var description:String){}
第一次寫這個bean的時候,我寫錯了,無論如何,在路由里面,就是無法取到該bean構造后的值。看了官方文檔才知道,這個類是需要加一個data前綴的,這樣才會生成 equals ,hashCode ,Setter, Getter 等這些方法。
另外一些感想
主要代碼都在這里了,完整的代碼在這里。
用kotlin , 感覺很幸福,相見恨晚。
代碼寫的少,對java 是一種強力的補充。它和java之間,完全可以相互調用,是一門很不錯的語言,以后可以慢慢用起來了,還有很多東西值得去探索。
還有一點還是提一提吧,kotlin親爹jetbrains, Idea 親爹也是 jetbrains。Idea對自家的這門語言支持的相當的不錯,大家都可以試試。另: 放棄Eclipse吧,別再折磨自己啦。
好久沒寫這么長的文章了,現在國內kotlin相關的東西也不多,希望這篇能有點作用。國內好多文字都隨意轉載,經常見到不署名原作作者地址的。知識靠傳播才能影響更多人,也希望大家轉載的話標注一下文章的原地址。
