标签
| Scala 微服务
作者
| 张逸
特别说明:
本文包含大量代码片段,若要获得更好阅读观感,请点击文末“阅读原文”或访问我的博客。
由于Scala本身属于JVM下的语言,因此它能够较好地与Java项目融合在一起。在Scala中调用Java库,基本上与在Java中调用Java库的方式是相同的(反过来则未必,必将Java没有Scala中独有的语法糖)。因此,在Scala中可以非常方便地调用Spring Cloud,使其支持Spring Cloud提供的微服务基础设施,例如Eureka、Feign以及Spring Boot等。
不过仍然有几点需要注意,这些方面包括:
-
Maven依赖
-
Spring的语法
-
Json的序列化
Maven依赖
在Scala项目中,如果仍然使用Maven管理依赖,则它与在Java项目中添加Spring Boot依赖几乎完全相同,不同在于项目要支持Scala,需要添加对Scala语言库的依赖:
1 2 3 4 5
|
<dependency> <groupId>org.scala-langgroupId> <artifactId>scala-libraryartifactId> <version>2.11.11version> dependency>
|
要支持用ScalaTest编写单元测试,则还需要添加:
1 2 3 4 5 6
|
<dependency> <groupId>org.scalatestgroupId> <artifactId>scalatest_2.11artifactId> <version>3.0.4version> <scope>testscope> dependency>
|
同时,添加对编译Scala代码的插件依赖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
|
<build> <plugins> <plugin> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-maven-pluginartifactId> plugin> <plugin> <groupId>org.codehaus.mojogroupId> <artifactId>build-helper-maven-pluginartifactId> <version>1.10version> <executions> <execution> <id>add-sourceid> <phase>generate-sourcesphase> <goals> <goal>add-sourcegoal> goals> <configuration> <sources> <source>src/main/scalasource> sources> configuration> execution> <execution> <id>add-test-sourceid> <phase>generate-test-sourcesphase> <goals> <goal>add-test-sourcegoal> goals> <configuration> <sources> <source>src/test/scalasource> sources> configuration> execution> executions> plugin> <plugin
> <groupId>net.alchim31.mavengroupId> <artifactId>scala-maven-pluginartifactId> <version>3.2.2version> <executions> <execution> <goals> <goal>compilegoal> <goal>testCompilegoal> goals> execution> executions> plugin> plugins> build>
|
Spring的语法
Scala语言中照样可以使用Java的Annotation,因此scala项目的Application,可以这样实现:
1 2 3 4 5 6 7
|
@SpringBootApplication @EnableDiscoveryClient class SqlEngineApplication
object SqlEngineApplication extends App { SpringApplication.run(classOf[SqlEngineApplication], args: _*) }
|
注意,Spring Cloud以及Spring Boot提供的annotation是运用在类上面的,而Scala可以运用的Application则可以直接定义为与类同名的object。
而对于Spring Boot的Controller,在语法上有少许差异,即在值中要使用Scala的
Array
类型,例如:
1 2 3 4 5 6 7 8 9
|
@RestController @RequestMapping(Array("/")) class SqlStatementController extends SqlGenerator { @RequestMapping(value = Array("/sql"), method = Array(GET)) def getSql:String = ???
@RequestMapping(value = Array("/sql"), method = Array(POST)) def generateSql(@RequestBody request: GenerateSqlRequest): String = ??? }
|
Json的序列化
添加依赖
Spring Boot使用Jackson作为Json的序列化支持,若要在Scala项目也要使用Jackson,则需要添加jackson对scala的支持模块:
1 2 3 4 5
|
<dependency> <groupId>com.fasterxml.jackson.modulegroupId> <artifactId>jackson-module-scala_2.11artifactId> <version>2.8.7version> dependency>
|
添加WebConfig
同时还需要添加WebConfig,告诉Spring Boot选择Scala Module对对象进行映射:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
@Configuration class WebConfig extends WebMvcConfigurerAdapter {
override def configureMessageConverters(converters: java.util.List[HttpMessageConverter[_]]): Unit = converters.add(jackson2HttpMessageConverter())
@Bean def jackson2HttpMessageConverter(): MappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(objectMapper())
@Bean def objectMapper(): ObjectMapper = new ObjectMapper() { setVisibility(PropertyAccessor.FIELD, Visibility.ANY) registerModule(DefaultScalaModule) } }
|
对多态的支持
客户端发过来的Request中,包含了一棵表达式树。这棵树的节点分为两种类型:
-
Condition Group
-
Condition
Condition Group作为根节点,可以递归嵌套Condition Group和Condition,如下图所示:
在Scala中的定义如下所示:
1 2 3 4 5 6 7 8 9
|
case class GenerateSqlRequest(sqlTemplateName: String, criteria: Option[ConditionGroup] = None, groupBy: List[GroupByField] = Nil)
abstract class ConditionExpression { def evaluate: String }
case class ConditionGroup(logicOperator: String, conditions: List[ConditionExpression]) extends ConditionExpression
case class Condition(fieldName: String, operator: String, values: List[String], dataType: String) extends ConditionExpression
|
GenerateSqlRequest
中包含的criteria属性的类型就是前面提及的表达式树,它对应的Json结构需要支持Json类型的多态,即前面代码所示的
ConditionExpression
抽象类型,子类
ConditionGroup
与
Condition
拥有不同的属性定义。要支持这种Json的多态,则必须在抽象类型
ConditionExpression
上添加如下annotation:
1 2 3 4 5 6 7 8 9 10 11
|
@JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
|