(這是一篇遲來的文章,從3月份計劃到成文花了5個月多……之後須要避免這樣的低效率。)java
以前寫第一篇文章時,只是想試試在Spring中使用Scala。但如今隨着工做的須要,已經決定在應用層基於Spring boot進行開發。後面的數據服務和數據整合部分將採用Akka。做者是一個Scala粉,但不腦殘。鑑於團隊、招人及社區生態多方面考慮,總體使用Scala技術棧仍是比較困難的。以前就有考慮過把Spring和Scala結合起來。後來瞭解到挖財的技術選型,他們就是基於Spring和Scala的,還開源了不少不錯的Spring Boot加強插件。這堅決了我以前的想法,也有了我5個月後續寫第2篇的能量。git
對於Scala還不熟悉的朋友能夠先看看《寫給Java程序員的Scala入門教程》,好對Scala有個初步映像。程序員
第一篇文章是基於Maven作項目配置的,如今換成了Gradle。緣由?Spring官方默認都是基於Gradle了,並且如今不少大型的Java項目都是基於Gradle進行構建了。如:Android、Kafka(Linkdin總體採用Gradle)。再加上我是一個比較愛折騰的人,既然如今有時間,爲何不試試Gradle呢?github
代碼在這裏:https://github.com/yangbajing/spring-boot-scala,此次不但把構建工具換成了Gradle,還一步到位使用了多項目的構建方式,這樣更符合真實開發的場景。 **注意:在build.gradle配置中,須要從新設置Scala和Java源碼的搜索路徑,把Java源碼路徑移動Scala的搜索路徑來。否則編譯時會遇到Java代碼找不到Scala代碼符號問題 **web
sourceSets { main { scala { srcDirs = ['src/main/scala', 'src/main/java'] } java { srcDirs = [] } } test { scala { srcDirs = ['src/test/scala', 'src/test/java'] } java { srcDirs = [] } } }
Spring Boot默承認以自動轉換JSON數據格式到Java類型或反之,但怎樣支持Scala數據類型呢?其實很簡單,只須要加入jackson-module-scala
依賴:spring
compile("com.fasterxml.jackson.module:jackson-module-scala_$scalaLibVersion:2.8.0.rc2")
並添加jacksonModuleScala
Bean 便可:json
@Bean public Module jacksonModuleScala() { return new DefaultScalaModule(); }
如今,咱們就能夠在Spring中自由的使用case class
、Scala Collection
、Option
等類型和數據結構,甚至還能夠和Java類型混合使用。好比咱們把Java類型嵌入到Scala的case class裏。api
User.java數組
public class User { private String name; private String nickname; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } }
Message.scala數據結構
case class Message(name: String, age: Int, user: User, status: Option[Boolean]) { @BeanProperty val createdAt: LocalDateTime = LocalDateTime.now() }
Scala控制器 (ApiController.scala):
@RequestMapping(path = Array("message"), method = Array(RequestMethod.POST)) def message(@RequestBody reqMsg: Message): Seq[Message] = { List( reqMsg, reqMsg.copy(age = reqMsg.age + 1, status = Some(true)) ) }
使用Scala編寫Spring控制器方法,有些和Java不同的地方和Scala的慣用法:
@RequestMapping
註解的path
髮型是一個數組類型,在Scala中須要顯示傳入一個數組類型的參數:Array("message")
。return
顯示返回數據,也不推薦使用return
。另外,若在Java代碼中使用Scala的數據類型。如:case class。在Java中必需使用new
關鍵字進行實例化,像Scala那樣直接經過類名實例化是不支持的。
Java控制器(WebController.java):
@RequestMapping(path = "message", method = RequestMethod.POST) public Message message(@RequestBody User user) { return new Message("Yang Jing", 30, user, new Some(false)); }
測試效果以下:
$ curl -XPOST -H 'content-type: application/json;utf8' -d '{"user":"楊景","nickname":"羊八井"}' http://localhost:18080/web/message {"name":"Yang Jing","age":30,"user":{"name":null,"nickname":"羊八井"},"status":false,"createdAt":"2016-08-25T17:22:50.841"} $ curl -XPOST -H 'content-type: application/json;utf8' -d '{"name":"yangbajing","age":30,"user":{"name":"楊景","nickname":"羊八井"}}' http://localhost:18080/api/message [{"name":"yangbajing","age":30,"user":{"name":"楊景","nickname":"羊八井"},"status":null,"createdAt":"2016-08-25T17:26:03.352"},{"name":"yangbajing","age":31,"user":{"name":"楊景","nickname":"羊八井"},"status":true,"createdAt":"2016-08-25T17:26:03.352"}]
Java 8開始,支持Lambda函數。可是Java的Lambda函數與Scala的函數類型是不兼容的(好消息是,從Scala 2.12開始,將兼容Java Lambda函數)。咱們可使用scala-java8-compat
這個庫來還算優雅的解決這個問題。
首先添加scala-java8-comat
依賴:
compile("org.scala-lang.modules:scala-java8-compat_$scalaLibVersion:0.7.0")
在Scala中訪問Java8 Function,可使用以下方式:
import scala.compat.java8.FunctionConverters._ def(@RequestParam name: Optional[String], ... name.orElseGet(asJavaSupplier(() => reqMsg.name))
除了顯示的使用asJavaSupplier
來轉換特定的Java8 Function,還可使用asJava
隱式轉換來自動轉換:
name.orElseGet((() => reqMsg.name).asJava)
也許你並不喜歡Scala,也不須要在Spring中使用Scala,Java 8也足夠。但我但願能爲你打開了一扇門,在JVM平臺上還有如此有意思的語言。