# 第九章 SpringBoot與Scala
### 項目代碼
本章項目代碼:
https://github.com/LightSwordSpringBoot/lightsword
### JVM上的語言家族
JVM上的語言
```
Scala
Java
Groovy
Jython
Kotlin
Fantom
Rhino
JRuby
Ceylon
Clojure
...
```
1) Clojure是一個函數編程語言(functional programming language),采用類LISP語法。在處理狀態方面很擅長,且其處理方式跟Java語言完全不同。盡管如此,在語法和功能上也跟Java相差太遠。
2) Groovy是一個支持可選類型的動態語言。強依賴于Java,在很多方面都跟Java極為類似,例如語法和代碼結構方面。這使得其非常容易上手使用。在腳本和web頁面這種需要動態元素和元程序元素(meta-programming element)方面Groovy比較擅長。其動態特性使得對于企業級服務器端核心業務邏輯開發并不是一個好的選擇。
3) Scala是一個OO/函數式語言,采用類C語法。深入研究之后,你會發現其函數特性更為明顯。這個特點會促使用戶去選擇更純粹的函數式業務邏輯解決方案。Scala采用靜態類型,其泛型設計更加具備圖靈完備性(Turing complete)。但是,用另外一門語言中的泛型來寫程序是否合理呢?如果想要說清楚Scala的所有特性,那還需要一篇文章的篇幅。簡單的說,Scala對于程序員來講束縛太多。盡管第一眼看過去Scala似乎提供了Java夢想提供給開發者的功能,但是一旦深入了解就會發現這是個大坑。Scala不具備成為主流編程語言的特點。
4) Fantom是一個面向對象語言,也包含函數元素,采用類C語法。Fantom在很多方面的方案都簡單明了,例如nullable類型,非可變性(immutability)和模塊設計。其靜態類型系統采用的方式很簡單。泛型僅僅在列表(List)、映射(Map)和函數(Function)中得到支持,開發者不能自己添加。在開發者需要添加的時候,Fantom會自己自動進行類型轉換。雖然Fantom包含主流語言應該包括的所有特性,但并沒有獲得相應的關注。其中一個疑問是,Fantom的類型系統足夠吸引開發者嗎?
...
這裡可聊的很多,在此不展開.
### Scala簡介
#### Scala的設計哲學
Object-Oriented Meets Functional---當面向對象遇上函數式:
Have the best of both worlds. Construct elegant class hierarchies for maximum code reuse and extensibility, implement their behavior using higher-order functions. Or anything in-between.
#### 典型特征
* 支持面向對象編程范式
* 支持函數式編程范式
* 語法動態簡潔表達力豐富
* 具備靜態強類型和豐富的泛型
#### Scala, A Scalable language
Scala,一個可擴展的語言.
Scala精心整合了面向對象和函數式編程語言。
#### 面向對象(Object-Oriented)
Scala是純種的面向對象的語言。從概念上講,每一個值都是一個對象,每一個操作都是一個方法調用。語言支持通過類和特征的高級組件架構。
面向對象編程是一種自頂向下的程序設計方法.萬事萬物都是對象,對象有其行為(方法),狀態(成員變量,屬性).
許多傳統的設計模式Scala已經原生支持。單例模式對應object對象定義,訪問者通過模式匹配支持。使用隱式類,Scala甚至允許你對現有類型類進行操作,無論他們來自Scala或java!
#### 函數式編程(Functional)
Scala也是骨子里透著函數式編程范式的語言。函數是一等公民(當然,這個在你使用面向對象編程范式風格的時候,這個函數公民也只能退后了),不可變數據結構庫等。不變性有助于編寫并發程序.
函數式編程方法通過組合和應用函數來構造邏輯系統.函數式編程傾向于把軟件分解為其需要執行的行為或操作,而且通常采用自底向上的方法.同時,函數式編程也提供了非常強大的對事物進行抽象和組合的能力.
Scala不固執己見;你可以自由使用任何你喜歡的風格。面對有多種不同需求的問題領域,你可以在一個解決方案的不同部分,采用最適合的編程方法.
除了命令式,函數式,還有哪些其他的編程范式?
### 與JVM的無縫集成(Seamless Java Interop)
Scala在JVM上運行。java和Scala類可以自由地混合,無論他們居住在不同項目或同一項目。Scala可以自由使用java庫、框架和工具。像Ant或Maven工具,IDE如Eclipse或NetBeans,框架,IntelliJ,Spring。
Scala可以運行在所有常見的JVM, 當然還有Android OS。
甚至,Scala都想把前端JavaScript的事情也給干了.
Scala社區是java生態系統的重要組成部分。流行的Scala框架,包括Akka, Finagle, and the Play web framework include dual APIs for Java and Scala.
函數式編程的思想是開發一個小的核心結構,可結合靈活的方式,然后進行組合。
---
更加豐富的scala介紹,可參考:
https://www.gitbook.com/book/universsky/scala_notes/details
### 使用SB與Scala開發
如果我們使用Scala語言進行開發(或者Java,Scala混合開發,參考開源項目Spark:https://github.com/apache/spark), 而對于SpringBoot來說, 可以使用任何JVM上的語言.
比如, 要使用Scala進行SpringBoot應用的開發, 需要添加Scala的相應依賴和編譯支持:
新建maven工程,添加pom依賴:
```
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.springboot.in.action</groupId>
<artifactId>lightsword</artifactId>
<version>1.0-SNAPSHOT</version>
<inceptionYear>2016</inceptionYear>
<properties>
<scala.version>2.11.8</scala.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<encoding>UTF-8</encoding>
<scala.compat.version>2.11</scala.compat.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<platform-bom.version>2.0.3.RELEASE</platform-bom.version>
<spring-boot-starter-bom.version>1.0.0-SNAPSHOT</spring-boot-starter-bom.version>
<spring-boot.version>1.3.5.RELEASE</spring-boot.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.5.RELEASE</version>
</parent>
<dependencies>
<!-- scala -->
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>
<dependency>
<groupId>com.typesafe.scala-logging</groupId>
<artifactId>scala-logging-slf4j_2.11</artifactId>
<version>2.1.2</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.specs2</groupId>
<artifactId>specs2-core_${scala.compat.version}</artifactId>
<version>2.4.16</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.scalatest</groupId>
<artifactId>scalatest_${scala.compat.version}</artifactId>
<version>2.2.4</version>
<scope>test</scope>
</dependency>
<!-- sb -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-velocity</artifactId>
</dependency>
<!--test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.7</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.7</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.2.0</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/scala</sourceDirectory>
<testSourceDirectory>src/test/scala</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<configuration>
<scalaVersion>${scala.version}</scalaVersion>
<launchers>
<launcher>
<id>app</id>
<mainClass>com.springboot.in.action.LightSwordApplication</mainClass>
<jvmArgs>
<jvmArg>-Xmx1024m</jvmArg>
</jvmArgs>
</launcher>
</launchers>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
</execution>
</executions>
<configuration>
<includeScope>system</includeScope>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<configuration>
<downloadSources>true</downloadSources>
<buildcommands>
<buildcommand>ch.epfl.lamp.sdt.core.scalabuilder</buildcommand>
</buildcommands>
<additionalProjectnatures>
<projectnature>ch.epfl.lamp.sdt.core.scalanature</projectnature>
</additionalProjectnatures>
<classpathContainers>
<classpathContainer>org.eclipse.jdt.launching.JRE_CONTAINER</classpathContainer>
<classpathContainer>ch.epfl.lamp.sdt.launching.SCALA_CONTAINER</classpathContainer>
</classpathContainers>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<configuration>
<scalaVersion>${scala.version}</scalaVersion>
</configuration>
</plugin>
</plugins>
</reporting>
<repositories>
<repository>
<id>scala-tools.org</id>
<name>Scala-Tools Maven2 Repository</name>
<url>http://scala-tools.org/repo-releases</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>scala-tools.org</id>
<name>Scala-Tools Maven2 Repository</name>
<url>http://scala-tools.org/repo-releases</url>
</pluginRepository>
</pluginRepositories>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.spring.platform</groupId>
<artifactId>platform-bom</artifactId>
<version>${platform-bom.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
```
其中build中的maven-scala-plugin是編譯期依賴,scala代碼需要scala的compiler,所以在maven構建過程中,使用一個編譯scala代碼的maven插件.這是typesafe(scala背後的公司)的工程師Josh Suereth開發的,遵循maven插件開發規範.
然後,org.scala-lang:scala-library是Scala應用運行時的依賴.
這樣,我們就可以像使用SB+java一樣來使用SB+scala來開發了.
### 實現一個RESTful接口
一個最簡單的例子:
lightsword/src/main/scala/com/springboot/in/action/controller/HelloController.scala代碼:
```
package com.springboot.in.action.controller
import java.util.Date
import com.alibaba.fastjson.JSONObject
import org.springframework.web.bind.annotation.{RequestMapping, RequestParam, RestController}
/**
* 系統內部測試用
*/
@RestController
class HelloController {
@RequestMapping(Array("/hello"))
def greeting(@RequestParam(value="name", defaultValue="LightSword") name: String) = {
val now = new Date
val content = "Hello, " + name + "! Now is: " + now
val json = new JSONObject
json.put("conent", content)
json
}
}
```
瀏覽器訪問:
http://localhost:9527/hello
可以看到輸出
```
{"conent":"Hello, LightSword! Now is: 當天的日期"}
```