从架构师视角看是否该用Kotlin做服务端开发?
前言
自从Oracle收购Sun之后,对Java收费或加强控制的尝试从未间断,谷歌与Oracle围绕Java API的官司也跌宕起伏。虽然Oracle只是针对Oracle JDK8的升级收费,并释放了OpenJDK一直开源这份善意,但是如果没有各个大非Oracle的JVM、JDK和众多其它基于JVM的语言,Oracle这份善意能维持到什么时候可不好说。
大厂要从JVM和JDK的层面早做打算,而广大中小企业,就只能先从Java语言的层面,先找到Oracle以外的备胎。自从被谷歌钦定为Android开发首选语言之后,采用Apache2.0 License的Kotlin逐渐进入大众的视野。从领域语言到通用语言,基于JVM的语言选择众多,Kotlin能脱颖而出被谷歌相中,除了License的友好外,自然有其独到之处(个人觉得基于Python语法的Jython在当时也算是一个强强联合的选择,当然现在来看Kotlin的优势明显)。我们先从摘自网上的一个段子来感受下Kotlin的特点:
- Scala:想解决Java表达能力不足的问题
- Groovy:想解决Java语法过于冗长的问题
- Clojure:想解决Java没有函数式编程的问题
- Kotlin:想解决Java
当然,团队开发比个人开发要考虑的问题更多,本文从研发团队的视角,来审视在服务端开发项目中是否应该使用Kotlin。而关于Kotlin语言细节,详情请大家参移步Kotlin官方文档或搜索引擎去了解更多,本文就不详细展开。
选择Kotlin的理由
1. 与Java近乎完美的兼容
作为团队技术预研,我已小试牛刀使用Kotlin做了一小一中真实上线项目,包括CI\CD、Spring全家桶、MyBatis、RDMS、NoSQL、消息队列、微服务、RESTful接口、鉴权、计费等全面实践,除项目代码之外的也用Kotlin实现了爬虫、运维、测试工具等。这其中除了BenchMark性能测试因OpenJDK:jmh直接操作字节码而需要编译比java略复杂外,所有事件都能做到人(Java)无我(Kotlin)有,人(Java)有我(Kotlin)强,只身十天左右就能高质量从零完成一套AI SaaS服务的开发、测试。
我自身的实践可以说明,Kotlin使用Java的开源组件、类库、工具栈上与Java代码本身几乎(仅发现jmh有些许差别外)没有差别,这是其它基于JVM的语言无法比拟有优势。
2. 更易写出可靠的代码
2.1 强制非空校验
// 所有变量默认不能为空
var a: String = "abc"
a = null //编译错误
// 如果想要允许变量为空,变量声明中的类型以"?"结尾
var b: String? = null
// 非空类型的变量,可以直接调用对象方法
println(a.length)
// 可空类型的变量,可以用"?."替换"."来进行完全调用
println(b?.length) //如果b为null,则b?.length返回null,而不会抛出NPE
// "?."链式调用更显安全调用的威力
bob?.department?.head?.name //只要有一环为null,则整个表达式返回null
// 空类型不能直接赋值给非空类型
a = b //编译错误
// 可以使用非空断言"!!"来强制将b转换为其对应的非空类型
a = b!! //如果b为null,则抛出NPE,否则赋值成功
// 更安全的做法是使用"?:"赋值
a = b?:"Default"
// "?:"还可以接”throw“、"return"、"break"语句来提前结束函数或语句块
a = b?: throw MyException("自定义异常")
可以看到,Kotlin通过强制非空校验机制,规避了Java最易犯的NPE问题,并且在不额外增加判空代码的情况下,很容易写出空安全的代码,当然也通过非空断言保留了抛出NPE的途径,具体请参考Kotlin官方文档-空安全
2.2 只读变量
// 通过val可以方便声明只读变量,防止变量的误修改,类似于java的final
val a = "Hello"
//...
a = "world" //编译错误
// var用于声明可读写变量
var b = 1 //如果下方没有代码对b进行修改,则编译告警
声明只读变量的语法更简洁,配合var未修改编译告警,强制开发人员明白无误地声明变量
3. 简洁高效,代码量可减少逾50%
3.1 默认参数与数据类
// 默认参数,大多数情况可以代替函数重载
fun a(name: String, age: Int = 0) {
//...
}
// data关键字会自动为类型增加hashCode(),equals(),copy(),toString()方法
// val关键字会自动生成只读成员变量及get方法
// var关键字会自动生成成员变量及get和set方法
data class Person(val id: Int, var name: String, var age: Int = 0)
//以上就是Person实体的完整声明,它等价于十行以上的java代码
默认参数、数据类、主构造函数的组合使用,让我们节省了90%以上的实体声明代码,且表达能力更清晰,可维护性更强
3.2 类型推断
when(val u = request.session.getAttribute("user")){
is String -> println(u.toUpperCase())
is Map<*, *> -> println(u["name"])
!is List<*> -> println(u)
}
在类型判断代码之后,变量会自动转换为需要的类型,省去变量声明和类型转换代码。
除此之外Kotlin拥有着丰富的集合操作,以及类扩展、更简洁的lambda、字符串模板、操作符重载、解构等等几乎包含了现在语言的所有优良特征,让业务代码节省50%以上,且表义能力更强。
4. Spring的加成
Spring-Boot已经将kotlin提升为仅次于Java的推荐语言,足以见得Spring对Kotlin重视
4.1 构造函数注入
Spring构造函数注入配置kotlin的主构造函数语法,Spring+Kotlin天生就很搭
@Service
class SMSService(
@Value("\${sms.appId}") private val appId: String,
@Value("\${sms.appSecret}") private val appSecurity: String,
private val webClient: WebClient
) {
//...
}
4.2 针对Kotlin的扩展
Spring中还有部分专门针对Kotlin的扩展如: SpringApplicationExtension:
@SpringBootApplication
class SpringBootApplicationStarter
fun main(args: Array<String>) {
//拉起Spring-Boot应用
runApplication<SpringBootApplicationStarter>(*args)
}
4.3 针对Kotlin的示例代码
Spring文档专门为Kotlin编写了示例代码,这可是Spring支持的老牌语言Groovy多年都没享受到的待遇:
Kotlin天生对Spring的友好,以及Spring对Kotlin的重视和加成,可以预见Kotlin未来在服务器开发领域的地位会越来越高。
选择Kotlin需要考虑的问题
1. IDE&工具链
Kotlin首选开发工具非同属jetbrain公司的IDEA莫属
Eclipse+Kotlin Plugin也是不错的选择
二者Kotlin的开发体验都不输于Java的开发体验
不过Eclipse的Kotlin插件存在两个问题:
1) Debug不能自动识别Kotlin的main方法,需要手工填入Class名;
2) Debug不支持Kotlin编译的allopen选项,所有Bean和Configuration类型须显示声明为open
@SpringBootApplication
open class SpringBootApplicationStarter
@Configuration
open class BeanConfig
@Service
open class TestService
@RestController
open class TestController
在IDE之外,Kotlin能完美地运行于Maven和Gradle之上,CI/CD工具Jenkins自然不在话下,JUnit、Swagger等Java原有工具链运行起来都没有差别,在字节码和jar包之外的各种工具能否运行,理论上并不取决于项目代码是Kotlin还是Java;可能最大的问题在于,原有的代码检查工具不能再继承使用。
2. 编码规范
Kotlin放开了很多限制如:
- 可以声明全局变量和全局方法
- 一个文件可以有多个类
- 可以通过扩展来为已有类增加新方法
这带来了诸多便利,如同一领域的实体可以声明在一个文件中:
//WebResponses.kt
open class WebResponse(var errorCode: Int = 0, var errorMsg: String? =www.yasenyulee.cn null)
open class DataResponse<T>(var data: T?) : WebResponse()
open class MapResponse<K, V>(map: Map<K, V>) : DataResponse<Map<K, V>>(map) {
constructor(vararg pairs: Pair<K, V>) : this(mutableMapOf(*pairs))
}
可以很方便地扩展已有类库
// StringExtensions.kt
val REGEX_Digits = Regex(www.shentuylgw.cn"\\\d+")
fun String.isDigits(): Boolean {
return this.matches(REGEX_Digits)
}
// ACLInterceptor
val appId = request.getParameter("appId")
if (!appId.isNullOrBlank() && appId.isDigits()) {
//...
}
但是如果不加限制,很难保证开发人员不随心所欲。所以应该针对Kotlin项目制定一些编码规范(可以与Android项目共用),如:
- 只有数据实体可以定义在同一个文件中
- 类型扩展应以”Extensions“后缀结尾,如"StringExtensions"
- 业务逻辑不应出现在全局方法中,应按照一类一文件的方式组织
- 业务逻辑的扩展,不应使用Kotlin类型扩展机制,而应使用接口、抽象类、子类
3. 人员技能
对Java开发人员来说,学习Kotlin并不是什么困难的事情,以我个人的经验,直接以一个小项目开始的情况下,一周之内编码效率就会明显超过Java。
当然,语言工具不是项目成败的决定性因素,使用工具的人才是,不妨先让项目组中的核心成员去了解下Kotlin,再做决定。
快速上手建议从Koans开始,想要更深入地掌握,还是要阅读Kotlin官方文档
4. 依赖大小
在Spring项目中使用Kotlin会使依赖增加4.18MB左右
这在Spring-Boot兴起之前,对我来说确实是个问题,6年前一个war包也就3~5MB大小,还要为Tomcat能部署多个服务而不会代码空间溢出,把war包进一步瘦身——把公共jar包都放入Tomcat/lib之下。
而现在Spring-Boot项目动则30MB起步的大小,让我已经不再考虑服务器资源的问题,而更享受各种组件和工具带来的开发效率提升。
当然这个问题,因项目实际情况而异,需要交给作为架构师的你自己去决策。
5. BenchMark
具体细节本不是此文的讨论目标,且对Kotlin做BenchMark性能测试不是那么困难,但是Kotlin的BenchMark资料也不多,这里我附送一个简明教程:
- 步骤1:使用maven生成Kotlin benchmark工程
mvn archetype:generate www.wangffzc.cn \
-DinteractiveMode=false \
-DarchetypeGroupId=org.openjdk.jmh www.yixingylzc.cn \
-DarchetypeArtifactId=jmh-kotlin-benchmark-archetype \
-DgroupId=org.sample www.sangyulpt.com\
-DartifactId=test \
-Dversion=1.0
- 步骤2:编写测试代码
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
open class MyBenchmark www.jinniuyulzc.cn{
@Benchmark
fun testMethod1(www.jiniuyLzc.cn ) {
//...
}
@Benchmark
fun testMethod2(www.baishenjzc.cn ) {
//...
}
}
- 步骤3:编译&运行
mvn clean package
java www.henxingyule.com -jar target/benchmarks.jar
更多性能测试详情,可参考
- OpenJDK:jmh官网
- kotlin-benchmarks项目
哪些项目类型适合使用Kotlin
- 现有Java项目,不推荐;
- 小型或验证型项目,强烈推荐;
- 微服务项目,推荐先局部试点,逐步完善编码规范和人员技能;
- 大型单体项目,有较强较稳定的核心团队时推荐,人员主靠招聘时不推荐;
- 公共组件,不推荐,除专门用于Kotlin的组件外。
结语
不管怎么说,语言工具不是项目成败的决定性因素,使用工具的人才是,不妨先让项目组中的核心成员去了解下Kotlin,再做决定。
从架构师视角看是否该用Kotlin做服务端开发?的更多相关文章
- 成为JAVA架构师必看书籍推荐
原创文章 “学习的最好途径就是看书“,这是我自己学习并且小有了一定的积累之后的第一体会.个人认为看书有两点好处: 1.能出版出来的书一定是经过反复的思考.雕琢和审核的,因此从专业性的角度来说,一本好书 ...
- Java架构师必看,超详细的架构师知识点分享!
在Java程序员行业中,有不少Java开发人员的理想是成为一名优秀的Java架构师,Java架构师的主要任务不是从事具体的软件程序的编写,而是从事更高层次的开发构架工作.他必须对开发技术非常了解,并且 ...
- 【架构师之路】APP架构师必看:面对爆发流量如何进行架构调整
一.APP架构与WEB架构的最大不同 移动APP的架构和传统PC的WEB架构有三点不同: 1.连接的稳定性.在传统的web端连接成功后就可以认为它是稳定的,但在移动端.无线端,APP连接非常敏感,可能 ...
- Java架构师必看的10本书
1.大型网站系统与JAVA中间件实践 本书围绕大型网站和支撑大型网站架构的Java中间件的实践展开介绍. 从分布式系统的知识切入,让读者对分布式系统有基本的了解:然后介绍大型网站随着数据量.访问量增长 ...
- 你真的了解微服务架构吗?听听八年阿里架构师怎样讲述Dubbo和Spring Cloud微服务架构
微服务架构是互联网很热门的话题,是互联网技术发展的必然结果.它提倡将单一应用程序划分成一组小的服务,服务之间互相协调.互相配合,为用户提供最终价值.虽然微服务架构没有公认的技术标准和规范或者草案,但业 ...
- 听听八年阿里架构师怎样讲述Dubbo和Spring Cloud微服务架构
转自:https://baijiahao.baidu.com/s?id=1600174787011483381&wfr=spider&for=pc 微服务架构是互联网很热门的话题,是互 ...
- go语言游戏服务端开发(一)——架构
五邑隐侠,本名关健昌,12年游戏生涯. 本教程以Go语言为例. 网络游戏程序分为客户端和服务端.客户端负责图形渲染.交互和一些简单校验处理,服务端负责业务逻辑处理.数据存储. 我们开发一个游戏de ...
- .NET应用架构设计—服务端开发多线程使用小结(多线程使用常识)
有一段时间没有更新博客了,最近半年都在着写书<.NET框架设计—大型企业级框架设计艺术>,很高兴这本书将于今年的10月份由图灵出版社出版,有关本书的具体介绍等书要出版的时候我在另写一篇文行 ...
- Java 架构师之路(2)
一.技术 J2EE技术是架构师的基础.1.<Java编程思想> 初学Java时阅读这本书觉得好难,阅读第二遍时才觉得讲的很细致.这是一本不怕多读的好书. 2.<J2EE应用与BEA ...
随机推荐
- 火狐中添加selenium IDE
在火狐中添加selenium IDE 1.下载selenium IDE,此处下载的是selenium-ide-2.5.0.xpi 2.在火狐中,打开菜单-->附加组件-->用户附加组件的工 ...
- Luogu P3263 [JLOI2015]有意义的字符串
Link 设\(e=\frac{b+\sqrt d}2,i=\frac{b-\sqrt d}2\). 显然\(f_n=e^n+i^n\)是一个整数,且\(f_n=(e+i)f_{n-1}+eif_{n ...
- git使用问题二删除远程仓库文件,本地保留不动
git rm --cached filename/-r directory git commit "xxxx" git push
- WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! 的解决办法
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ WARNING: REMOTE HOST IDENTIFICATION HAS ...
- postProcessBeanFactory方法源码跟踪
看这篇文章之前可以先了解之前的跟踪流程,https://www.jianshu.com/p/4934233f0ead 代码过宽,可以shift + 鼠标滚轮 左右滑动查看 AbstractApplic ...
- jq插件和jq
封装一个jq (function(win) { var jQuery = function(selecter) { this.version = '1.0.1'; //版本号 this.selecte ...
- 从零开始学C++(1 变量和基本类型)
接下来的几篇文章介绍C++的基础知识点. C++是一种静态数据类型语言,它的类型检查发生在编译时.因此,编译器必须知道程序中每一个变量对应的数据类型. 数据类型是程序的基础:它告诉我们数据的意义以及我 ...
- input中name和id的区别
一直很困惑,表单里面input标签有id和name,它们之间到底有什么区别自己很少去想,只知道一般的场景该怎么使用,今天就在网上搜索了一下,自己也总结一下.为什么有了ID还要有Name呢?其实ID就像 ...
- SQLserver 存储过程生成任意进制/顺序流水号
ALTER PROCEDURE [dbo].[TentoSerial] @num int, @ret nvarchar(10) output AS declare @StringXL nvarc ...
- Centos 8下普通用户增加root权限
问题: 解决: 重启Centos,使用root登陆: