一文搞懂Kotlin符号处理接口KSP

2024-04-18 00:28

本文主要是介绍一文搞懂Kotlin符号处理接口KSP,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

公众号「稀有猿诉」        原文链接 一文搞懂Kotlin符号处理接口KSP

Kotlin符号处理(Kotlin Symbol Processing)即KSP是可以用于开发轻量级编译器插件的一套API。是Kotlin原生的,Kotlin语法友好的编译器插件。使用简单且易于上手,可以实现一些非常强大的编译时代码处理功能,如代码生成和代码检查。今天就来学习一下KSP的基本原理,以及如何使用KSP API。

注意,本文是Kotlin中较为高级的话题,适合有一定的Kotlin基础的同学,否则理解起来可能有难度,可以事先阅读前面的文章。

什么是KSP

与前文提到的注解处理器kapt类似,KSP也是一种编译时的插件,能够在编译前处理Kotlin语言的符号。KSP API能地道地处理Kotlin的源码,因为它是专门为Kotlin而设计的,能够完全的理解和识别Kotlin的语言符号,以及Kotlin专属的特性:如扩展函数,声明点泛型变化以及本地函数。KSP API基于Kotlin的语法,把Kotlin程序拆解为各种静态的符号,可以处理如类,成员,函数,参数 以及注解等等。但它并不是运行时的(那是反射做的事情),因此像逻辑如循环和条件语句是没有办法进行处理,以及也无法得到表达式的结果。

虽然KSP是编译器插件,但它是运行在最终编译之前,也就是说在编译器编译全部代码之前,事先会运行KSP插件。所以KSP API最适合做的事情是:

  1. 读取代码和各种资源文件,并进行分析
  2. 生成代码

接下来看如何具体使用KSP API。

配置KSP

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

KSP是由谷歌开发的一套工具,包括两部分一个是Kotlin plugin,另一个是依赖库。所以需要在项目的根build.gradle里面,先把plugin添加到项目里:

// The root build.gradle of your project
plugins {id 'org.jetbrains.kotlin.jvm' version '1.9.23' apply falseid 'com.google.devtools.ksp' version '1.9.23-1.0.20' apply false
}

当然,这一步其实并不是必须的,也可以在每个模块中再配置plugin。

接下来,在使用KSP的模块里面添加plugin,添加依赖以及指明KSP processor,这是最为关键的配置:

// module build.gradle
plugins {id 'org.jetbrains.kotlin.jvm'id 'com.google.devtools.ksp'
}dependencies {implementation project(':kspannotation')ksp project(':kspprocessor')
}

如果项目顶层指定了plugin的版本,那么到了module这里,就不必再指定版本了。另外就是要注意版本的匹配,ksp的版本前半段『1.9.23』指明 的是最低的Kotlin版本要求。最好是让ksp要求的版本与指定的Kotlin版本匹配或者差距较小,否则可能会有问题。dependencies中的ksp指定的是KSP processor,对于有些库可能注解和定义和KSP的processor可能会在同一个包里,那么写一句就够了,如Room的,就一句:ksp ‘androidx.room:room-compiler:2.6.1’。

如果是自定义的processor,需要为processor单独建一个library module,配置ksp库为依赖即可:

// KSP processor module build.gradle
plugins {id 'org.jetbrains.kotlin.jvm'
}dependencies {implementation project(':kspannotation')implementation 'com.google.devtools.ksp:symbol-processing-api:1.9.23-1.0.20'implementation 'com.squareup:kotlinpoet-ksp:1.16.0'
}

典型的KSP procesor(包括网上大部分的例子)都是分了三个module,一个是定义注解的module,一个是实现processor的,一个是使用注解和processor的。但这并不是必须的,为了方便,其实把注解的定义和processor放在一个module就可以了。只要把processor与使用它的module分开来了,就可以。

**注意:**对于processor module来说它的类型要是library,并且要是Java library或者Kotlin library,因为这是Kotlin语言层面的东西。对于Android同学来说在新建module时一定要选择『Java or Kotlin Library』。

实现KSP Processor

配置好了模块后,剩下的就是要实现一个KSP processor了。

实现Processor

大部分工作plugin已经做好了,我们需要做的就是实现一些接口。有两个需要实现,一个是SymbolProcessorProvider,另一个则是SymbolProcessor。

SymbolProcessorProvider相当于是processor的一个工厂方法,我们实现它的create方法,返回一个SymbolProcessor实例,一个典型的实现:

class MyProcessorProvider : SymbolProcessorProvider {override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {return MyProcessor(environment.codeGenerator)}
}

它就相当于一个工厂方法,把上下文环境传给processor,SymbolProcessor是重点,我们需要实现它的process方法,针对感兴趣的符号进行处理,比如用KotlinPoet生成代码,这里是发挥创造力的地方:

class MyProcessor(private val generator: CodeGenerator) : SymbolProcessor {override fun process(resolver: Resolver): List<KSAnnotated> {val annotatedClasses = resolver.getSymbolsWithAnnotation(MyAnnotation::class.java.name).filterIsInstance<KSClassDeclaration>()for (aclass in annotatedClasses) {val packageName = aclass.packageName.asString()val className = aclass.simpleName.asString()val methods = aclass.getDeclaredFunctions())// ...}return emptyList()}
}

注册Processor

实现了process后还需要把process注册一下,否则ksp plugin无法找到这个processor。在processor module与代码同级文件夹下新建文件『resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider』,然后把刚才实现的provider的完整类名,写在文件里,如果是使用IDE一般都会有提示的。

// myprocessor/src/main/
//    |-- kotlin/net/toughcoder/
//              |-- MyProcessorProvider.kt
//              |-- MyProcessor.kt
//    |-- resources/META-INF/services/
//              |-- com.google.devtools.ksp.processing.SymbolProcessorProvider
// file: resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
net.toughcoder.MyProcessorProvider

为啥要用KSP

目前来说KSP最主要应用仍然 是注解的处理,以及配合注解进行代码生成。通过前面一篇关于注解的文章中我们知道,注解的处理已经有了一个专门的工具了叫做kapt,就目前来说KSP能做的事情kapt也都能做,它们都是用于编译时代码处理以及代码生成,都能处理注解。那么,在已经有了kapt的前提下,为啥还要搞KSP呢?

kapt虽然是Kotlin的注解处理器,但是它保持Java的兼容性,它直接复用了Java的AbstractProcessor,要依赖于Java的annotation procssor以及javac,只适用于Kotlin/JVM,其他target用不起来,因此它并不能算是Kotlin原生的工具,对Kotlin的特性支持不友好。再有就是,为了保持与javac的兼容性,它的处理速度很慢,必须先把Kotlin代码转成javac能认识的标准Java代码,这肯定会有不必要的性能开销。基于这些限制,kapt已经停止开发了,处于维护状态了,不会再添加新功能了。省流点来说,kapt是以Java角度来看待输入代码的(即也要处理的源码),而KSP是以Kotlin角度

KSP则是Kotlin原生的,基于Kotlin开发的,且是为了Kotlin开发的,并不受限于javac,因此所有的Kotlin目标平台都能用。并且对Kotlin的特性支持的很友好。它的处理速度也较kapt有提升,因为不必要做编码转换了,省了一道工序。从官方给出的数据看至少能省25%的编译时间。另外,KSP的API使用起来更加的Kotlin友好一些SymbolProcessor传递过来的Resolver有很方便的接口可以取得被标的类,而且符号对象是KSClassDeclaration,它可以方便的取一个Kotlin类的相关的其他符号,如包名,类名,方法等。

总结

通过本文我们理解了KSP的概念,并学会了如何在项目中配置KSP, 以及如何实现一个KSP processor。KSP视Kotlin代码为一系列的静态符号,对Kotlin语言特性支持友好,处于活跃的开发状态且被官方大力支持,因此应该尽早转向KSP。并且相信KSP能做的事情会越来越多。

参考资料

  • Kotlin Symbol Processing API
  • Migrate from kapt to KSP
  • Write a Symbol Processor with Kotlin Symbol Processing
  • An Introduction to Kotlin Symbol Processing
  • Kotlin Symbol Processing

欢迎搜索并关注 公众号「稀有猿诉」 获取更多的优质文章!

原创不易,「打赏」「点赞」「在看」「收藏」「分享」 总要有一个吧!

这篇关于一文搞懂Kotlin符号处理接口KSP的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/913286

相关文章

Deepseek R1模型本地化部署+API接口调用详细教程(释放AI生产力)

《DeepseekR1模型本地化部署+API接口调用详细教程(释放AI生产力)》本文介绍了本地部署DeepSeekR1模型和通过API调用将其集成到VSCode中的过程,作者详细步骤展示了如何下载和... 目录前言一、deepseek R1模型与chatGPT o1系列模型对比二、本地部署步骤1.安装oll

MyBatis-Flex BaseMapper的接口基本用法小结

《MyBatis-FlexBaseMapper的接口基本用法小结》本文主要介绍了MyBatis-FlexBaseMapper的接口基本用法小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具... 目录MyBATis-Flex简单介绍特性基础方法INSERT① insert② insertSelec

Spring排序机制之接口与注解的使用方法

《Spring排序机制之接口与注解的使用方法》本文介绍了Spring中多种排序机制,包括Ordered接口、PriorityOrdered接口、@Order注解和@Priority注解,提供了详细示例... 目录一、Spring 排序的需求场景二、Spring 中的排序机制1、Ordered 接口2、Pri

Idea实现接口的方法上无法添加@Override注解的解决方案

《Idea实现接口的方法上无法添加@Override注解的解决方案》文章介绍了在IDEA中实现接口方法时无法添加@Override注解的问题及其解决方法,主要步骤包括更改项目结构中的Languagel... 目录Idea实现接China编程口的方法上无法添加@javascriptOverride注解错误原因解决方

使用C++将处理后的信号保存为PNG和TIFF格式

《使用C++将处理后的信号保存为PNG和TIFF格式》在信号处理领域,我们常常需要将处理结果以图像的形式保存下来,方便后续分析和展示,C++提供了多种库来处理图像数据,本文将介绍如何使用stb_ima... 目录1. PNG格式保存使用stb_imagephp_write库1.1 安装和包含库1.2 代码解

C#使用DeepSeek API实现自然语言处理,文本分类和情感分析

《C#使用DeepSeekAPI实现自然语言处理,文本分类和情感分析》在C#中使用DeepSeekAPI可以实现多种功能,例如自然语言处理、文本分类、情感分析等,本文主要为大家介绍了具体实现步骤,... 目录准备工作文本生成文本分类问答系统代码生成翻译功能文本摘要文本校对图像描述生成总结在C#中使用Deep

Spring Boot 整合 ShedLock 处理定时任务重复执行的问题小结

《SpringBoot整合ShedLock处理定时任务重复执行的问题小结》ShedLock是解决分布式系统中定时任务重复执行问题的Java库,通过在数据库中加锁,确保只有一个节点在指定时间执行... 目录前言什么是 ShedLock?ShedLock 的工作原理:定时任务重复执行China编程的问题使用 Shed

一文详解Java Condition的await和signal等待通知机制

《一文详解JavaCondition的await和signal等待通知机制》这篇文章主要为大家详细介绍了JavaCondition的await和signal等待通知机制的相关知识,文中的示例代码讲... 目录1. Condition的核心方法2. 使用场景与优势3. 使用流程与规范基本模板生产者-消费者示例

Redis如何使用zset处理排行榜和计数问题

《Redis如何使用zset处理排行榜和计数问题》Redis的ZSET数据结构非常适合处理排行榜和计数问题,它可以在高并发的点赞业务中高效地管理点赞的排名,并且由于ZSET的排序特性,可以轻松实现根据... 目录Redis使用zset处理排行榜和计数业务逻辑ZSET 数据结构优化高并发的点赞操作ZSET 结

微服务架构之使用RabbitMQ进行异步处理方式

《微服务架构之使用RabbitMQ进行异步处理方式》本文介绍了RabbitMQ的基本概念、异步调用处理逻辑、RabbitMQ的基本使用方法以及在SpringBoot项目中使用RabbitMQ解决高并发... 目录一.什么是RabbitMQ?二.异步调用处理逻辑:三.RabbitMQ的基本使用1.安装2.架构