使用KSP处理注解和生成Kotlin代码

 前言

ksp是Kotlin 符号处理工具,类似于kapt(apt),可用于开发轻量级编译器插件,github地址:google/ksp: Kotlin Symbol Processing API (github.com)

也可以理解为注解处理和编译时代码生成工具(也能处理所有文件,不止被注解标记的)

既然有了kapt(apt),为什么还要有ksp?

  1. 速度比kapt更快,性能更好
  2. 对kotlin的支持更好
  3. 适用于Kotlin Multiplatform
  4. 可以直接生成kotlin代码文件(实际上可以生成任意类型的文件)

ps:ksp和kapt(apt)一样,都只能生成新代码文件,无法修改源代码

正文

如何创建一个自己的ksp处理器呢,我们开始一步一步的接入并处理注解然后生成一个.kt文件

1.创建ksp model

首先需要你先有一个支持kotlin的现有项目或空项目

然后new一个Kotlin Library

ksp model的build.gradle.kts增加ksp的依赖:

plugins {
    id("java-library")
    id("org.jetbrains.kotlin.jvm")
}

java {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
}

dependencies {
    implementation("com.google.devtools.ksp:symbol-processing-api:1.7.10-1.0.6")//引入ksp
}

ps:ksp最新版本查看(需要对应当前使用的kotlin版本):Releases · google/ksp (github.com)

2.新建ksp处理器并注册

创建TestKsp.kt,修改如下代码:

package com.lt.ksp

import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.processing.SymbolProcessorProvider
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSVisitorVoid
import com.google.devtools.ksp.validate

/**
 * creator: lt  2022/10/20  lt.dygzs@qq.com
 * effect : ksp处理程序的创建
 * warning:
 */
internal class TestKspSymbolProcessorProvider : SymbolProcessorProvider {
    override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor =
        TestKspSymbolProcessor(environment)
}

/**
 * creator: lt  2022/10/20  lt.dygzs@qq.com
 * effect : ksp处理程序
 * warning:
 */
internal class TestKspSymbolProcessor(private val environment: SymbolProcessorEnvironment) : SymbolProcessor {
    override fun process(resolver: Resolver): List<KSAnnotated> {
        val symbols = resolver.getSymbolsWithAnnotation(TestKsp::class.qualifiedName!!)
        val ret = mutableListOf<KSAnnotated>()
        symbols.toList().forEach {
            if (!it.validate())
                ret.add(it)
            else
                it.accept(TestKspVisitor(environment), Unit)//处理符号
        }
        //返回无法处理的符号
        return ret
    }
}

/**
 * creator: lt  2022/10/20  lt.dygzs@qq.com
 * effect : 访问并处理相应符号
 * warning:
 */
internal class TestKspVisitor(private val environment: SymbolProcessorEnvironment) : KSVisitorVoid() {
    override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
        //todo 在这里处理
    }
}

/**
 * creator: lt  2022/10/20  lt.dygzs@qq.com
 * effect : 测试注解
 * warning:
 */
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
annotation class TestKsp

创建特定路径的文件,用来指定ksp处理器的创建器的全类名(注册ksp)

ps:路径: ksp\src\main\resources\META-INF\services\com.google.devtools.ksp.processing.SymbolProcessorProvider

文件内写上创建器的全类名

 3.在app model中引入你的ksp model

在你的app model下的build.gradle.kts中修改:

plugins {
    ...
    id("com.google.devtools.ksp") version "1.7.10-1.0.6"//引入ksp插件
}

//如果是安卓项目
android {
    ...
    buildTypes {
        release {
            ...
            kotlin {
                sourceSets.main {
                    //增加代码目录(将ksp生成的代码加入代码目录中)
                    kotlin.srcDir("build/generated/ksp/release/kotlin")
                }
            }
        }
        debug {
            ...
            kotlin {
                sourceSets.main {
                    kotlin.srcDir("build/generated/ksp/debug/kotlin")
                }
            }
        }
    }
    kotlin {
        sourceSets.test {
            kotlin.srcDir("build/generated/ksp/test/kotlin")
        }
    }
}
//如果是kotlin项目
sourceSets {
    main {
        java.srcDirs("build/generated/ksp/main/kotlin")
    }
}

dependencies {
    ...
    implementation(project(":ksp"))//引入刚才新建的ksp model
    ksp(project(":ksp"))
}

4.处理注解并生成代码

我们给MainActivity加上TestKsp注解,然后创建一个MyMainActivity.kt,然后创建一个字段,中间保存MainActivity所有的字段名和类型

 修改TestKspVisitor如下:

/**
 * creator: lt  2022/10/20  lt.dygzs@qq.com
 * effect : 访问并处理相应符号
 * warning:
 */
internal class TestKspVisitor(private val environment: SymbolProcessorEnvironment) :
    KSVisitorVoid() {
    override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
        val packageName = classDeclaration.containingFile!!.packageName.asString()//获取这个类的包名
        val originalClassName = classDeclaration.simpleName.asString()//获取类名
        val className = "My${originalClassName}"
        val file = environment.codeGenerator.createNewFile(//创建新的文件(默认.kt)
            Dependencies(
                true,
                classDeclaration.containingFile!!
            ), packageName, className
        )
        file.write("package $packageName\n\n".toByteArray())//写入文件
        file.write("class $className {\n".toByteArray())
        file.write("    val fields = ".toByteArray())
        val fields = classDeclaration.getAllProperties().map {//遍历所有的属性
            val name = it.simpleName.getShortName()//属性名
            val type = it.type.resolve().toString()//属性类型
            "$name: $type"
        }.joinToString()
        file.write("\"$fields\"\n".toByteArray())
        file.write("}".toByteArray())
        file.close()
    }
}

然后我们运行项目,就可以自动生成代码了,我们去到build文件夹中找到生成的代码

 我们就可以直接使用生成的代码了

补充

日志打印无法使用println,且无法使用调试(可能是我没找到方法),而是使用:

environment.logger.warn("log")

而且是需要使用warn,不然找不到日志,其次日志是在build栏里看到的:

结语

可以看到,其实使用起来也不是特别麻烦,而有了这个功能后,又可以做很多骚操作了

参考:

KSP 快速入门|科特林 (kotlinlang.org)

google/ksp: Kotlin Symbol Processing API (github.com)

api参考:

Java annotation processing to KSP reference | Kotlin (kotlinlang.org)

示例:

ltttttttttttt/Buff: Add status to beans in Compose, Fields in beans can be directly used as the State<T> (github.com)

ksp/examples/playground at main · google/ksp (github.com)

对Kotlin或KMP感兴趣的同学可以进Q群 101786950

如果这篇文章对您有帮助的话

可以扫码请我喝瓶饮料或咖啡(如果对什么比较感兴趣可以在备注里写出来)

<think>好的,用户问的是纯Java项目是否可以使用KSPKotlin Symbol Processing)来生成代码。首先,我需要确认KSP的基本特性使用环境。KSPKotlin的符号处理工具,主要用于在编译阶段处理Kotlin代码生成新的代码或资源。通常,KSPKotlin项目结合使用,因为它依赖于Kotlin的编译器符号处理API。 接下来,用户提到的是纯Java项目。这里需要考虑Java项目是否能够集成KSP。由于KSP本身是为Kotlin设计的,它的API插件机制都是围绕Kotlin编译器构建的。Java项目虽然可以通过Kotlin混合项目的方式间接使用Kotlin代码,但纯Java项目可能无法直接利用KSP,因为KSP需要处理的是Kotlin的符号,而Java的符号处理机制不同。 然后,我需要思考是否有变通方法。例如,是否可以在Java项目中添加Kotlin的依赖,并配置KSP插件。理论上,如果项目允许引入Kotlin编译插件,可能可以配置KSP处理Kotlin代码,即使主代码Java。但这样就不再是“纯”Java项目了,变成了混合项目。此外,生成代码如果是Kotlin的,可能还需要确保Java代码能够正确调用这些生成Kotlin类,可能需要处理互操作性问题。 另外,用户可能需要了解替代方案。如果纯Java项目无法使用KSP,可以考虑其他Java注解处理器,如APT(Annotation Processing Tool),这是Java原生的代码生成工具,广泛用于生成代码,比如Lombok库。APT在Java生态中成熟且稳定,适合纯Java项目。 还需要验证一些技术细节,比如KSP是否只能在Kotlin代码上运行,或者是否能处理Java代码。根据官方文档,KSP主要处理Kotlin代码,对Java的支持有限。因此,如果项目中没有Kotlin文件,KSP可能无法发挥作用。 最后,总结回答时需要明确说明纯Java项目无法直接使用KSP,但可以通过引入Kotlin依赖配置来间接使用,同时建议考虑Java原生的APT作为替代方案。这样用户可以根据项目需求选择合适的方法。</think>### 纯 Java 项目能否使用 KSP 生成代码? #### 1. **KSP 的基本特性** Kotlin Symbol Processing(KSP)是 Kotlin 的符号处理工具,主要用于在编译时分析 Kotlin 代码生成代码。它的核心依赖是 **Kotlin 编译器**,设计目标是为 Kotlin 代码提供比传统注解处理器(如 Java APT)更高效、更准确的符号处理。 #### 2. **纯 Java 项目的限制** - **语言依赖**:KSP 直接处理的是 Kotlin 的符号(如 Kotlin 类、函数、属性),**无法直接解析 Java 代码**。如果项目中只有 Java 代码KSP 无法获取有效输入。 - **编译环境**:KSP 需要 Kotlin 编译器插件支持,而纯 Java 项目通常不包含 Kotlin 编译环境。即使手动添加 Kotlin 依赖,仍需混合 Kotlin 代码才能触发 KSP。 #### 3. **变通方案(非纯 Java)** 若允许项目中引入 Kotlin,可通过以下步骤间接使用 KSP: 1. **添加 Kotlin 依赖**: ```gradle plugins { id "org.jetbrains.kotlin.jvm" version "1.9.0" // 最低需要 Kotlin 1.4.0 } ``` 2. **配置 KSP 插件**: ```gradle plugins { id "com.google.devtools.ksp" version "1.9.0-1.0.13" } ``` 3. **编写 KSP 处理器**(需用 Kotlin 实现): ```kotlin class ExampleProcessor : SymbolProcessor { override fun process(resolver: Resolver) { // 处理 Kotlin 符号并生成代码 } } ``` 4. **生成代码需兼容 Java**:生成Kotlin 类需添加 `@JvmStatic` 等注解,确保 Java 能调用。 #### 4. **纯 Java 项目的替代方案** - **Java 注解处理器(APT)**: - 使用 `javax.annotation.processing` 实现,支持纯 Java 项目。 - 示例工具:Lombok、MapStruct、Google Auto 等。 - 配置简单,无需 Kotlin 依赖。 - **字节码操作库**(如 ASM、Byte Buddy): - 在编译后直接修改字节码,但需熟悉 JVM 指令。 #### 5. **对比总结** | 方案 | 适用语言 | 复杂度 | 生成阶段 | 典型工具 | |---------------|------------|--------|---------------|------------------| | KSP | Kotlin | 中 | 编译时(Kotlin) | KSP 插件 | | Java APT | Java | 低 | 编译时(Java) | Lombok, Auto | | 字节码操作 | 任何 JVM 语言 | 高 | 编译后 | ASM, Byte Buddy | #### 6. **结论** - **纯 Java 项目无法直接使用 KSP**,因为 KSP 依赖 Kotlin 编译器符号。 - **替代方案**: - 如果允许引入 Kotlin,可通过混合编程间接使用 KSP。 - 如果坚持纯 Java,优先选择 Java APT 或字节码操作库。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值