Scala開發者的Spring-Boot快速上手指南 02:Scala慣用法

(這是一篇遲來的文章,從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

第一篇文章是基於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 = []
            }
        }
    }

支持Scala數據類型

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 classScala CollectionOption等類型和數據結構,甚至還能夠和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的慣用法:

  • 註解屬性爲數組時,Scala必需顯示使用數組形式傳參。如@RequestMapping註解的path髮型是一個數組類型,在Scala中須要顯示傳入一個數組類型的參數:Array("message")
  • Scala中,方法返回值類型是能夠自動推導的。但在寫Spring控制器方法時推薦顯示註明返回類型。
  • Scala的全部表達式都有值,且代碼塊最後一個表達式的值就是代碼塊的值。這樣,在Scala的函數裏不須要使用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 Function 和 Scala Function[N]

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平臺上還有如此有意思的語言。

相關文章
相關標籤/搜索