Skip to content

KSP 快速入門指南

//: # (description: 將基於 Kotlin Symbol Processing (KSP) 的註解處理器新增到你的專案,或使用 KSP API 建立你自己的處理器。)

在本指南中,你將學習:

  • 如何將基於 KSP 的註解處理器新增到你的專案。
  • 如何使用 KSP API 建立你自己的註解處理器。
  • 在哪裡可以找到處理器產生的程式碼。

將基於 KSP 的處理器新增到你的專案

若要在你的專案中使用外部處理器,請將 KSP 新增到 build.gradle(.kts) 檔案中的 plugins {} 區塊。如果僅在特定模組中需要該處理器,請改為將其新增到該模組的 build.gradle(.kts) 檔案中:

kotlin
// build.gradle.kts

plugins {  
    kotlin("jvm") version "2.3.0"  
    id("com.google.devtools.ksp") version "2.3.3"
}
groovy
// build.gradle

plugins {
    id 'org.jetbrains.kotlin.jvm' version '2.3.0'
    id 'com.google.devtools.ksp' version '2.3.3'
}

若要尋找 KSP 的最新版本,請查看 GitHub Releases

在頂層 dependencies {} 區塊中,新增你要使用的處理器。本範例使用 Moshi,但其他處理器的處理方式相同:

kotlin
// build.gradle.kts

dependencies {
    ksp("com.squareup.moshi:moshi-kotlin-codegen:1.15.2")
}
groovy
// build.gradle

dependencies {
    ksp 'com.squareup.moshi:moshi-kotlin-codegen:1.15.2'
}

建立你自己的處理器

透過以下步驟,你將建立一個簡單的註解處理器,用以產生 helloWorld() 函式。雖然這在實踐中沒什麼用,但它展示了建立你自己的處理器和註解的基礎知識。

將 KSP 新增到專案

建立一個新的 Kotlin 專案並新增 KSP 外掛程式:

  1. 在 IntelliJ IDEA 中,選擇 File | New | Project

  2. 在左側清單中,選擇 Kotlin

  3. 選擇 Gradle 作為建置系統,然後點擊 Create

    建立新專案

  4. 將 KSP 外掛程式新增到 build.gradle(.kts) 檔案中:

    kotlin
    // build.gradle.kts
    
    plugins { 
        kotlin("jvm") version "2.3.0"
        id("com.google.devtools.ksp") version "2.3.3" apply false
    }
    groovy
    // build.gradle
    
    plugins {
        id 'org.jetbrains.kotlin.jvm' version '2.3.0'
        id 'com.google.devtools.ksp' version '2.3.3' apply false
    }

建立一個註解

在專案根目錄建立一個新模組並宣告一個註解:

  1. 選擇 File | New | Module

  2. 在左側清單中,選擇 Kotlin

  3. 指定以下欄位並點擊 create

    • Name:annotations
    • Build system:Gradle

    建立新模組

  4. 在該模組中,建立 HelloWorldAnnotation.kt 檔案並宣告一個名為 HelloWorldAnnotation 的註解:

    kotlin
    // annotations/src/main/kotlin/com/example/annotations/HelloWorldAnnotation.kt
    
    package com.example.annotations
    
    annotation class HelloWorldAnnotation

建立並註冊處理器

  1. 在專案根目錄建立另一個名為 processor 的模組。

  2. 在該模組的 build.gradle(.kts) 檔案中,將 KSP API 和你宣告的註解新增為相依性:

    kotlin
    // processor/build.gradle.kts
        
    plugins {  
        kotlin("jvm")
    }
        
    dependencies {
        implementation(project(":annotations"))
        implementation("com.google.devtools.ksp:symbol-processing-api:2.3.6")  
    }
    groovy
    // processor/build.gradle
    
    plugins {
        id 'org.jetbrains.kotlin.jvm'
    }
    
    dependencies {
        implementation project ':annotations'
        implementation 'com.google.devtools.ksp:symbol-processing-api:2.3.6'
    }
  3. 在 processor 模組中,建立一個新的 HelloWorldProcessor.kt 檔案並新增以下程式碼:

    kotlin
    // processor/src/main/kotlin/HelloWorldProcessor.kt
    
    class HelloWorldProcessor(val codeGenerator: CodeGenerator) : SymbolProcessor {
        // 1️⃣ process() 函式
        override fun process(resolver: Resolver): List<KSAnnotated> {
            resolver
                .getSymbolsWithAnnotation("com.example.annotations.HelloWorldAnnotation")
                .filter { it.validate() }
                .filterIsInstance<KSFunctionDeclaration>()
                .forEach { it.accept(HelloWorldVisitor(), Unit) }
        
           return emptyList()
        }
        
       // 2️⃣ Visitor
       inner class HelloWorldVisitor : KSVisitorVoid() {
           override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: Unit) {
               createNewFileFrom(function).use { file ->
                   file.write(
                       """
                           fun helloWorld(): Unit {
                               println("Hello world from function generated by KSP")
                           }
                       """.trimIndent()
                   ) 
               } 
           } 
       }
            
       // 3️⃣ createNewFileFrom() 函式
       private fun createNewFileFrom(function: KSFunctionDeclaration): OutputStream { 
           return codeGenerator.createNewFile(
              dependencies = createDependencyOn(function),
              packageName = "",
              fileName = "GeneratedHelloWorld"
           )
       }
        
       // 3️⃣ createDependencyOn() 函式
       private fun createDependencyOn(function: KSFunctionDeclaration): Dependencies {
           return Dependencies(aggregating = false, function.containingFile!!)
       }
    }
    
    // 用於將字串寫入 OutputStream 的公用函式
    fun OutputStream.write(string: String): Unit {
        this.write(string.toByteArray())
    }

    新增 IDE 建議的匯入內容。請確保從 com.google.devtools.ksp.processing 匯入 ResolverDependencies 類別。或者,將以下幾行複製到 HelloWorldProcessor.kt 的頂部:

    kotlin
    // processor/src/main/kotlin/HelloWorldProcessor.kt
    
    import com.google.devtools.ksp.processing.CodeGenerator
    import com.google.devtools.ksp.processing.Dependencies
    import com.google.devtools.ksp.processing.Resolver
    import com.google.devtools.ksp.processing.SymbolProcessor
    import com.google.devtools.ksp.symbol.KSAnnotated
    import com.google.devtools.ksp.symbol.KSFunctionDeclaration
    import com.google.devtools.ksp.symbol.KSVisitorVoid
    import com.google.devtools.ksp.validate
    import java.io.OutputStream

    讓我們來看看這段程式碼:

    • 1️⃣ process() 函式包含處理器的主要邏輯。它會取得所有被 HelloWorldAnnotation 標註的符號,並為每個符號呼叫 HelloWorldVisitor

      process() 函式會傳回一個未處理符號清單,以便在下一輪中處理。在本範例中,它安全地傳回 emptyList()。如需更多資訊,請參閱多輪處理

    • 2️⃣ 處理器使用訪問者遍歷 KSP 的 Kotlin 抽象語法樹 (AST) 檢視。在 HelloWorldProcessor 類別中,HelloWorldVisitor 類別即為訪問者。由於 HelloWorldAnnotation 僅用於函式,因此僅覆寫了 visitFunctionDeclaration()

      KSVisitorVoid 是 KSP 提供的訪問者類別之一,你可以對其進行覆寫和調整。你也可以透過實作 KSVisitor<D, R> 介面 來建立自己的訪問者。

    • 3️⃣ createNewFileFrom() 會建立 KSP 產生程式碼的檔案。createDependencyOn() 使輸出檔案相依於使用該註解的原始碼檔案。

      若要進一步了解 KSP 如何建立和管理檔案,請造訪 CodeGenerator 介面 的原始碼。

  4. 建立 HelloWorldProcessorProvider.kt 檔案。在其中宣告一個繼承自 SymbolProcessorProviderHelloWorldProcessorProvider 類別:

    kotlin
    // processor/src/main/kotlin/HelloWorldProcessorProvider.kt
    
    import com.google.devtools.ksp.processing.SymbolProcessor
    import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
    import com.google.devtools.ksp.processing.SymbolProcessorProvider
    
    class HelloWorldProcessorProvider : SymbolProcessorProvider {  
        override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {  
            return HelloWorldProcessor(environment.codeGenerator)  
        }  
    }
  5. 註冊處理器提供者。在 resources/META-INF/services 目錄中,建立一個 com.google.devtools.ksp.processing.SymbolProcessorProvider 檔案,並新增提供者的完全限定名稱:

    text
    ## processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
        
    HelloWorldProcessorProvider

使用你的處理器

現在你已準備好測試你的處理器。按照以下步驟建立一個用戶端模組,並讓你的處理器根據被標註的元素產生程式碼:

  1. 在專案根目錄建立一個名為 app 的模組。

  2. 在該模組的 build.gradle(.kts) 檔案中:

    • 將 KSP 外掛程式新增到 plugins {} 區塊。
    • 將你的處理器和註解新增到 dependencies {} 區塊。

    例如:

    kotlin
    // app/build.gradle.kts
    
    plugins {
        kotlin("jvm")
        id("com.google.devtools.ksp")
    }
    
    dependencies { 
        implementation(project(":annotations"))
        ksp(project(":processor"))
    }
    groovy
    // app/build.gradle
    
    plugins {
        id 'com.google.devtools.ksp'
    }
    
    dependencies {
        implementation project (':annotations')
        ksp project (':processor')
    }
  3. 在專案層級的 settings.gradle(.kts) 檔案中,確保所有子模組都已自動包含:

    kotlin
    // settings.gradle.kts
    
    include("annotations")
    include("app")
    include("processor")
    groovy
    // settings.gradle
    
    include 'processor'
    include 'annotations'
    include 'app'
  4. app 模組中,建立一個 Main.kt 檔案並新增以下程式碼:

    kotlin
    // app/src/main/kotlin/Main.kt
    
    import com.example.annotations.HelloWorldAnnotation
    
    @HelloWorldAnnotation
    fun main() {
        helloWorld()
    }

    main() 函式會呼叫 helloWorld(),即使此函式尚不存在。你的 IDE 會將 helloWorld() 標記為未定義的參考。這是預期的:當你建置並執行專案時,KSP 會產生 helloWorld() 函式。

  5. 執行程式。你將在主控台中看到 helloWorld() 函式的輸出:

    text
    Hello world from function generated by KSP

    KSP 會在 GeneratedHelloWorld.kt 檔案中產生程式碼:

    text
    app/build/generated/ksp/main/kotlin/GeneratedHelloWorld.kt

探索專案結構

你專案的最終檔案結構應如下所示:

text
.
├── app  
│   ├── build.gradle.kts  
│   └── src  
│       └── main  
│           └── kotlin  
│               └── Main.kt   
├── annotations  
│   ├── build.gradle.kts  
│   └── src  
│       └── main  
│           └── kotlin
|				└── com  
|	                └── example
|						└── annotations
|							└── HelloWorldAnnotation.kt  
├── processor  
│   ├── build.gradle.kts  
│   └── src  
│       └── main  
│           ├── kotlin  
│           │   ├── HelloWorldProcessor.kt  
│           │   └── HelloWorldProcessorProvider.kt  
│           └── resources/META-INF/services
|				└── com.google.devtools.ksp.processing.SymbolProcessorProvider 
├── build.gradle.kts  
└── settings.gradle.kts

你可能還有其他的檔案和目錄。

下一步