著名IDE廠商JetBrains開發的基於JVM的靜態類型編程語言,聲稱100% interoperable with Java。Kotlin是由工程師設計的,各類細節設計很是切合工程師的須要。語法近似Java和Scala,且已活躍在Android開發領域,被譽爲Android平臺的Swift。html
Kotlin能與Java混合使用,而且直接複用Java的生態系統(庫、框架、工具)。一個已有的Java項目,只需引用Kotlin的Maven/Gradle插件,以及引用Kotlin標準庫的依賴,就能夠逐漸摻入Kotlin代碼。你徹底能夠當它是a better Java。java
Kotlin的學習曲線極其平緩,學習量至關於一個框架。有經驗的程序員閱讀了文檔就能馬上用起來了。不信你看:mysql
舉幾個例子來講明Kotlin的優勢吧,上代碼:react
//句尾不用寫分號 // 自動推導變量類型,無需聲明 val a = "Hello" // 簡單的println println(a.length() == 5) // 不用寫new, 直接調構造函數 val b = String("Hello") // 字符串插值 "$a $b" == "Hello Hello" // if-else是表達式, 真方便! // ==至關於equals, 不再怕忘寫equals了! val oneOrTwo = if (a == "Hello") 1 else 2 // ===至關於Java的== (a === b) == false // Lambda用{}包起來,如有惟一參數,參數名默認爲it // 集合的函數式操做, 無需Java 8繁瑣的stream.collect(Collectors.toList()) listOf(-1, 0, 1).map{it + 1}.filter{it > 0} == listOf(1, 2) // ?. (null-safe調用) // ?: (用默認值給null兜底) val numStr = getNumberOrNull()?.toString() ?: "" // 自動關閉的資源 FileInputStream("MyFile").use { stream -> // 可指定參數名爲stream, 取代默認的it val firstByte = stream.read() } // 能夠更簡單,一行 val fileContent = File("MyFile").readText() // lazy, 延遲初始化 class CPU { val cpuCores by lazy { Runtime.getRuntime().availableProcessors() } }
Kotlin爲厭煩Java而疑慮Scala的人提供了避風港,爲喜歡Groovy而想要靜態類型的人提供了避風港。啊!生活。ios
Spring Boot是流行的Web快速開發框架,使基於Spring的開發更便捷。
咱們已經知道Spring很好用,而Spring Boot的設計目標是:git
Kotlin能輕鬆集成Spring Boot,用Java怎麼寫,用Kotlin基本上也怎麼寫。程序員
Spring能在線生成項目,免去建立項目的煩惱,請猛擊連接http://start.spring.io/ 。github
buildscript { ext { springBootVersion = '1.3.5.RELEASE' kotlinVersion = '1.0.4' } repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}") } } apply plugin: 'kotlin' apply plugin: 'spring-boot' jar { baseName = 'myapp' version = '0.1-SNAPSHOT' } sourceCompatibility = 1.8 targetCompatibility = 1.8 // class文件保留參數名稱 compileJava.options.compilerArgs.add '-parameters' compileTestJava.options.compilerArgs.add '-parameters' springBoot { mainClass = 'myapp.ApplicationKt' } dependencies { compile 'org.springframework.boot:spring-boot-starter-aop' compile 'org.springframework.boot:spring-boot-starter-web' compile "org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion}" }
@SpringBootApplication open class Application { @Bean open fun json(): MappingJackson2JsonView { return MappingJackson2JsonView(ObjectMapper()) } } fun main(args: Array<String>) { SpringApplication.run(Application::class.java, *args) }
Kotlin的函數可定義在類外面,而特殊的main函數要麼放在外面,要麼放在伴生對象(companion object)裏面。這裏就放在外面吧!web
你會發現class和fun前面有open修飾符,它的意思是非final,Kotlin默認一切都是final的,若是不想要final救要加上open。因爲Spring有時要建立代理,要求類和方法不能爲final,所以咱們每一處都寫上open,以避免忘記。spring
這裏只有一個json()方法,用來在Spring中初始化Jackson,這樣咱們就能使用JSON了。
@RestController @RequestMapping("/api/users") open class UserApi { @RequestMapping("/{id}", method = arrayOf(RequestMethod.GET)) open fun get(@PathVariable id: Long) = "User(id=$id, name=admin, password=123)" }
好簡單啊!如今,在IDE中運行Application.kt文件,就開始運行了!用瀏覽器打開http://localhost:8080/api/users/1
Spring Boot使用JPA很是簡單(照着官網的getting started學吧),但我要介紹另外一種ORM框架——Ebean,它模仿了Rails的Active Record,支持經常使用的JPA註解。值得一提的是,Ebean的做者也喜歡Kotlin。
須要一個配置文件src/main/resources/ebean.properties :
# 是否生成建表SQL ebean.db.ddl.generate=true # 是否執行建表SQL ebean.db.ddl.run=false datasource.db.username=DB用戶名 datasource.db.password=DB密碼 datasource.db.databaseUrl=jdbc:mysql://localhost:3306/你的database名稱 datasource.db.databaseDriver=com.mysql.jdbc.Driver
咱們對ebean.db.ddl.run(是否執行建表SQL)選擇了false。由於Ebean會生成建表SQL,咱們能夠手動執行,避免每次都從新建表,把數據丟棄了。編寫實體類後再運行,SQL會生成在項目目錄下,手動執行一下吧!(亦可在首次啓動前把ebean.db.ddl.run改爲true)
而後在Spring中初始化Ebean吧:
// 把這個方法添加到Application類 @Bean(autowire = Autowire.BY_TYPE) open fun getEbeanServer(): EbeanServer { val config = ServerConfig() config.name = "db" config.loadFromProperties() config.isDefaultServer = true return EbeanServerFactory.create(config) }
而後要修改main方法,在Spring以前先執行Ebean的agent,改寫實體類的字節碼:
fun main(args: Array<String>) { val packageName = "com.iostate.**" // 改爲你本身的包名,實體類要放在這個包裏面 if (!AgentLoader.loadAgentFromClasspath("avaje-ebeanorm-agent", "debug=1;packages=$packageName")) { System.err.println( "avaje-ebeanorm-agent not found in classpath - not dynamically loaded") } SpringApplication.run(Application::class.java, *args) }
Ebean須要執行agent來改寫字節碼(instrumenation),而Hibernate則選擇了給實體對象建立動態代理(dynamic proxy),都是爲了能對實體進行AOP操做。
instrumenation使用複雜,調試簡單;dynamic proxy使用簡單,調試複雜。各有千秋,我更認同改寫字節碼。
import javax.persistence.* import com.avaje.ebean.Model import com.avaje.ebean.annotation.WhenCreated import com.avaje.ebean.annotation.WhenModified import java.sql.Timestamp import com.avaje.ebean.annotation.SoftDelete import com.fasterxml.jackson.annotation.JsonIgnore @MappedSuperclass abstract class BaseModel : Model() { @Id @GeneratedValue var id: Long = 0 @Version var version: Long = 0 @WhenCreated var whenCreated: Timestamp? = null @WhenModified var whenModified: Timestamp? = null } @Entity class User ( var name: String = "", @JsonIgnore var password: String = "" @SoftDelete var deleted: Boolean = false ) : BaseModel() { companion object find : Find<Long, User>() }
第一個類是全部實體模型的基類,提供一些通用字段。id是自增主鍵,version是樂觀鎖的標誌,whenCreated是建立時間,whenModified是修改時間。有的變量類型以問號結尾,這個跟Swift語言是同樣的,表示可爲null(默認是非null的)。
第二類是User,行數不多,沒有繁瑣的getter/setter。@JsonIgnore的做用是防止敏感字段被泄露到JSON中,@SoftDelete的做用是軟刪除(數據不可見,但沒有真的刪除)。companion object find : Find<Long, User>()
提供了一組快捷查詢方法,如byId(id)
all()
。
如今把UserApi修改以下:
@RestController @RequestMapping("/api/users") open class UserApi { @RequestMapping("/{id}", method = arrayOf(RequestMethod.GET)) open fun get(@PathVariable id: Long) = User.byId(id) @RequestMapping("/new", method = arrayOf(RequestMethod.POST)) open fun create(@RequestParam name: String, @RequestParam password: String): User { return User(name, password).apply { save() } } }
get方法真正向數據庫作查詢了!增長了create方法來建立用戶!若是想用瀏覽器快速測試,把RequestMethod.POST改爲GET,輸入連接http://localhost:8080/api/users/new?name=admin&password=123 試試!
Spring Boot能把程序打包成jar直接運行,這是很方便羣衆的!可是JSP和Ebean在jar模式都沒法工做。
那麼在生產環境要怎麼解決呢?能夠把jar解壓運行!
參考文檔的exploded archives: http://docs.spring.io/spring-...
# 解壓 unzip -q myapp.jar # 運行 java org.springframework.boot.loader.JarLauncher # 生產模式用如下的nohup方式,以防程序隨着shell一塊兒關閉 nohup java org.springframework.boot.loader.JarLauncher &
我本身用的命令不同:
unzip -q myapp.jar nohup java -cp '.:./lib/*' com.myapp.ApplicationKt &
注意當前所在的工做目錄,日誌目錄/logs
會建立在當前工做目錄下。
我提供了一個示例項目,比較粗糙,請多多包涵 https://github.com/sorra/bms
老外也有幾個示例項目,可供參考:
Spring Boot Kotlin project with a REST Webservice and Spring Data: https://github.com/sdeleuze/s...
Demo Webapp using SpringBoot, Kotlin and React.js: https://github.com/winterbe/s...
順帶一提,輕境界就是用Kotlin + Spring Boot構建的!