首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >前端开发者的 Kotlin 之旅:KSP 代码生成实战

前端开发者的 Kotlin 之旅:KSP 代码生成实战

原创
作者头像
骑猪耍太极
发布2025-09-02 11:24:15
发布2025-09-02 11:24:15
20800
代码可运行
举报
运行总次数:0
代码可运行

作为前端开发者,你一定使用过 GraphQL Code Generator 从 Schema 自动生成 TypeScript 类型,或者用过 Prisma 从数据库 Schema 生成客户端代码。这些工具的核心思想就是编译时代码生成——在构建过程中分析源码,然后自动生成样板代码。今天,我们将深入 Kotlin 的 KSP (Kotlin Symbol Processing) 技术,看看如何在 Kotlin 中实现类似的编译时代码生成功能。通过一个完整的页面注册系统实战项目,你将掌握从注解定义到代码生成的完整流程。

前置知识回顾

在前面的学习中,我们已经掌握了:

第一部分:元编程基础

  • 理解了元编程的核心概念:用代码生成代码
  • 掌握了编译时 vs 运行时处理的区别
  • 了解了 Kotlin 元编程的技术栈

文章地址: https://cloud.tencent.com/developer/article/2554449

第二部分:反射深入

  • 学会了使用 KClass、KFunction、KProperty 进行运行时检查
  • 掌握了动态属性访问和方法调用
  • 理解了反射的性能开销

文章地址: https://cloud.tencent.com/developer/article/2558876

第三部分:注解系统

  • 学会了定义和使用自定义注解
  • 理解了注解的保留策略(SOURCE、BINARY、RUNTIME)
  • 掌握了运行时注解处理技术

文章地址: https://cloud.tencent.com/developer/article/2560699

现在,让我们将这些知识整合起来,构建一个真正的编译时代码生成系统。

什么是 KSP?

KSP 简介

KSP (Kotlin Symbol Processing) 是 Google 为 Kotlin 开发的轻量级编译器插件 API。它允许我们在编译时分析 Kotlin 代码的符号信息,并基于这些信息生成新的代码文件。

前端开发者的类比

如果你熟悉前端工具链,可以这样理解 KSP:

代码语言:javascript
代码运行次数:0
运行
复制
// 类似 GraphQL Code Generator
// 输入:GraphQL Schema
type User {
  id: ID!
  name: String!
  email: String!
}

// 输出:自动生成的 TypeScript 类型
export interface User {
  id: string;
  name: string;
  email: string;
}
代码语言:kotlin
复制
// KSP 的工作方式
// 输入:带注解的 Kotlin 类
@Page(name = "home", route = "/")
class HomePage {
    fun show() = println("首页")
}

// 输出:自动生成的注册器
object PageRegistry {
    fun createPage(name: String): Any? = when(name) {
        "home" -> HomePage()
        else -> null
    }
}

KSP vs 其他技术

特性

KSP

KAPT

反射

前端对比

处理时机

编译时

编译时

运行时

Babel 插件 vs 运行时 Proxy

性能

中等

构建时 vs 运行时开销

Kotlin 支持

原生

Java 兼容层

完整

TypeScript vs JavaScript

增量编译

N/A

Webpack 增量编译

项目需求分析

业务场景

假设我们正在开发一个多页面应用,需要一个页面注册系统来管理所有页面的创建和路由。传统做法需要手动维护一个注册表,每次添加新页面都要记得更新注册代码。

解决方案

使用 KSP 实现自动化的页面注册系统:

  1. 定义注解@Page 用于标记页面类
  2. 编写页面:使用注解标记各个页面类
  3. KSP 处理器:扫描所有 @Page 注解,生成注册器
  4. 使用生成的代码:在应用中使用自动生成的页面工厂

技术架构

代码语言:bash
复制
编译时:
源码 → KSP 扫描 → 代码生成 → 编译

运行时:
应用代码 → 调用生成的工厂 → 创建页面实例

第一步:定义页面注解

让我们从定义注解开始,这就像在前端定义组件的 props 接口一样:

代码语言:kotlin
复制
// src/main/kotlin/annotations/Page.kt
package annotations

/**
 * 页面注解 - 用于标记页面类
 * 类似前端框架中的组件注册装饰器
 */
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)  // 支持运行时访问(便于调试)
annotation class Page(
    /**
     * 页面名称 - 用作唯一标识符
     * 类似 React 组件的 displayName
     */
    val name: String = "",
    
    /**
     * 页面路由 - URL 路径
     * 类似 Vue Router 的 path
     */
    val route: String = "",
    
    /**
     * 页面描述 - 用于文档和调试
     */
    val description: String = "",
    
    /**
     * 优先级 - 用于排序显示
     * 数值越大优先级越高
     */
    val priority: Int = 0
)

设计要点

  1. @Target(AnnotationTarget.CLASS):只能用于类,避免误用
  2. @Retention(RUNTIME):支持运行时反射,便于调试和测试
  3. 丰富的元数据:提供足够的信息用于代码生成
  4. 合理的默认值:简化使用,提高开发体验

第二步:创建示例页面

现在让我们创建几个示例页面,就像在前端创建不同的组件一样:

代码语言:kotlin
复制
// src/main/kotlin/pages/HomePage.kt
package pages

import annotations.Page

/**
 * 首页 - 应用的主入口页面
 * 类似前端的 Home 组件
 */
@Page(
    name = "home",
    route = "/",
    description = "应用首页",
    priority = 100  // 最高优先级
)
class HomePage {
    fun showHome() {
        println("=== 欢迎来到首页 ===")
        println("这里是应用的主页面")
        println("提供应用的主要功能入口")
    }
    
    fun getPageInfo(): String = "HomePage - 应用首页"
}
代码语言:kotlin
复制
// src/main/kotlin/pages/AboutPage.kt
package pages

import annotations.Page

/**
 * 关于页面 - 展示应用信息
 */
@Page(
    name = "about",
    route = "/about",
    description = "关于我们页面",
    priority = 50
)
class AboutPage {
    fun showAbout() {
        println("=== 关于我们 ===")
        println("这是一个 Kotlin 元编程学习项目")
        println("展示 KSP 代码生成的强大功能")
    }
    
    fun getPageInfo(): String = "AboutPage - 关于我们"
}
代码语言:kotlin
复制
// src/main/kotlin/pages/ContactPage.kt
package pages

import annotations.Page

/**
 * 联系页面 - 提供联系方式
 */
@Page(
    name = "contact",
    route = "/contact",
    description = "联系我们页面",
    priority = 30
)
class ContactPage {
    fun showContact() {
        println("=== 联系我们 ===")
        println("邮箱: contact@example.com")
        println("电话: 123-456-7890")
    }
    
    fun getPageInfo(): String = "ContactPage - 联系我们"
}

页面设计特点

  1. 统一的注解使用:每个页面都用 @Page 标记
  2. 丰富的元数据:提供名称、路由、描述、优先级
  3. 一致的接口:都有 getPageInfo() 方法
  4. 业务逻辑封装:每个页面有自己的展示逻辑

第三步:构建 KSP 处理器

这是整个系统的核心部分,类似前端的 Babel 插件或 Webpack loader:

创建处理器子项目

首先,我们需要创建一个独立的处理器子项目,避免循环依赖:

代码语言:kotlin
复制
// processor/build.gradle.kts
plugins {
    kotlin("jvm")
}

dependencies {
    // KSP API - 用于编写处理器
    implementation("com.google.devtools.ksp:symbol-processing-api:1.9.20-1.0.14")
}

实现 KSP 处理器

代码语言:kotlin
复制
// processor/src/main/kotlin/PageProcessor.kt
package processor

import com.google.devtools.ksp.processing.*
import com.google.devtools.ksp.symbol.*
import com.google.devtools.ksp.validate
import java.io.OutputStream
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

/**
 * 页面注解处理器
 * 类似前端的代码生成器,扫描注解并生成代码
 */
class PageProcessor(
    private val codeGenerator: CodeGenerator,
    private val logger: KSPLogger
) : SymbolProcessor {

    override fun process(resolver: Resolver): List<KSAnnotated> {
        logger.info("🚀 开始处理 @Page 注解...")
        
        // 1. 查找所有带有 @Page 注解的类
        val pageAnnotationName = "annotations.Page"
        val symbols = resolver.getSymbolsWithAnnotation(pageAnnotationName)
        val ret = symbols.filter { !it.validate() }.toList()
        
        // 2. 过滤出有效的类声明
        val pageClasses = symbols
            .filterIsInstance<KSClassDeclaration>()
            .filter { it.validate() }
            .toList()
        
        if (pageClasses.isEmpty()) {
            logger.info("📝 没有找到带有 @Page 注解的类")
            return ret
        }
        
        logger.info("✅ 找到 ${pageClasses.size} 个页面类")
        pageClasses.forEach { clazz ->
            logger.info("   - ${clazz.simpleName.asString()}")
        }
        
        // 3. 生成 PageRegistry
        generatePageRegistry(pageClasses)
        
        return ret
    }
    
    /**
     * 生成页面注册器代码
     */
    private fun generatePageRegistry(pageClasses: List<KSClassDeclaration>) {
        logger.info("📦 开始生成 PageRegistry...")
        
        // 创建生成文件
        val file = codeGenerator.createNewFile(
            dependencies = Dependencies(false, *pageClasses.map { it.containingFile!! }.toTypedArray()),
            packageName = "generated",
            fileName = "PageRegistry"
        )
        
        file.use { outputStream ->
            outputStream.write(generateRegistryCode(pageClasses))
        }
        
        logger.info("🎉 成功生成 PageRegistry.kt")
    }
    
    /**
     * 生成注册器代码内容
     */
    private fun generateRegistryCode(pageClasses: List<KSClassDeclaration>): ByteArray {
        val currentTime = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
        
        // 提取页面信息
        val pageInfos = pageClasses.map { clazz ->
            val annotation = clazz.annotations.find { 
                it.shortName.asString() == "Page" 
            }
            
            val name = annotation?.arguments?.find { it.name?.asString() == "name" }?.value as? String ?: ""
            val route = annotation?.arguments?.find { it.name?.asString() == "route" }?.value as? String ?: ""
            val description = annotation?.arguments?.find { it.name?.asString() == "description" }?.value as? String ?: ""
            val priority = annotation?.arguments?.find { it.name?.asString() == "priority" }?.value as? Int ?: 0
            
            PageInfo(
                className = clazz.simpleName.asString(),
                packageName = clazz.packageName.asString(),
                name = name,
                route = route,
                description = description,
                priority = priority
            )
        }
        
        // 生成代码
        val code = buildString {
            appendLine("package generated")
            appendLine()
            
            // 导入语句
            val packages = pageInfos.map { it.packageName }.distinct()
            packages.forEach { pkg ->
                appendLine("import $pkg.*")
            }
            appendLine()
            
            // 文档注释
            appendLine("/**")
            appendLine(" * KSP 自动生成的页面注册器")
            appendLine(" * 生成时间: $currentTime")
            appendLine(" * 处理的页面数量: ${pageInfos.size}")
            appendLine(" */")
            
            // 注册器对象
            appendLine("object PageRegistry {")
            appendLine()
            
            // 页面元数据类
            appendLine("    data class PageMetadata(")
            appendLine("        val name: String,")
            appendLine("        val route: String,")
            appendLine("        val description: String,")
            appendLine("        val priority: Int,")
            appendLine("        val className: String")
            appendLine("    )")
            appendLine()
            
            // 页面工厂映射
            appendLine("    private val pageFactories = mapOf<String, () -> Any>(")
            pageInfos.sortedBy { it.name }.forEach { info ->
                appendLine("        \"${info.name}\" to { ${info.className}() },")
            }
            appendLine("    )")
            appendLine()
            
            // 页面元数据映射
            appendLine("    private val pageMetadata = mapOf<String, PageMetadata>(")
            pageInfos.sortedBy { it.name }.forEach { info ->
                appendLine("        \"${info.name}\" to PageMetadata(")
                appendLine("            name = \"${info.name}\",")
                appendLine("            route = \"${info.route}\",")
                appendLine("            description = \"${info.description}\",")
                appendLine("            priority = ${info.priority},")
                appendLine("            className = \"${info.packageName}.${info.className}\"")
                appendLine("        ),")
            }
            appendLine("    )")
            appendLine()
            
            // 公共 API
            appendLine("    /**")
            appendLine("     * 根据名称创建页面实例")
            appendLine("     */")
            appendLine("    fun createPage(name: String): Any? = pageFactories[name]?.invoke()")
            appendLine()
            
            appendLine("    /**")
            appendLine("     * 获取所有页面名称")
            appendLine("     */")
            appendLine("    fun getAllPageNames(): Set<String> = pageFactories.keys")
            appendLine()
            
            appendLine("    /**")
            appendLine("     * 获取页面元数据")
            appendLine("     */")
            appendLine("    fun getPageMetadata(name: String): PageMetadata? = pageMetadata[name]")
            appendLine()
            
            appendLine("    /**")
            appendLine("     * 获取所有页面元数据")
            appendLine("     */")
            appendLine("    fun getAllPageMetadata(): Collection<PageMetadata> = pageMetadata.values")
            appendLine()
            
            appendLine("    /**")
            appendLine("     * 根据路由查找页面")
            appendLine("     */")
            appendLine("    fun findPageByRoute(route: String): String? = ")
            appendLine("        pageMetadata.values.find { it.route == route }?.name")
            appendLine()
            
            appendLine("    /**")
            appendLine("     * 按优先级排序获取页面")
            appendLine("     */")
            appendLine("    fun getPagesByPriority(): List<PageMetadata> = ")
            appendLine("        pageMetadata.values.sortedByDescending { it.priority }")
            
            appendLine("}")
        }
        
        return code.toByteArray()
    }
    
    /**
     * 页面信息数据类
     */
    private data class PageInfo(
        val className: String,
        val packageName: String,
        val name: String,
        val route: String,
        val description: String,
        val priority: Int
    )
}

/**
 * KSP 处理器提供者
 * 类似前端插件的入口点
 */
class PageProcessorProvider : SymbolProcessorProvider {
    override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
        return PageProcessor(environment.codeGenerator, environment.logger)
    }
}

注册处理器

创建服务提供者配置文件:

代码语言:bash
复制
// processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
processor.PageProcessorProvider

第四步:配置构建系统

主项目配置

代码语言:kotlin
复制
// build.gradle.kts
plugins {
    kotlin("jvm")
    id("com.google.devtools.ksp") version "1.9.20-1.0.14"
    application
}

dependencies {
    // KSP API
    implementation("com.google.devtools.ksp:symbol-processing-api:1.9.20-1.0.14")
    
    // Kotlin 反射
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    
    // 使用处理器子项目
    ksp(project(":modules:kotlin-metaprogramming:processor"))
}

// KSP 配置
ksp {
    arg("generated.package", "generated")
}

// 源码目录配置 - 包含生成的代码
kotlin {
    sourceSets.main {
        kotlin.srcDir("build/generated/ksp/main/kotlin")
    }
}

// 应用配置
application {
    mainClass.set("demo.KspGeneratedDemoKt")
}

项目设置

代码语言:kotlin
复制
// settings.gradle.kts
include(":modules:kotlin-metaprogramming:processor")

第五步:使用生成的代码

现在让我们创建一个演示程序,使用 KSP 生成的页面注册器:

代码语言:kotlin
复制
// src/main/kotlin/demo/KspGeneratedDemo.kt
package demo

import generated.PageRegistry
import pages.*

/**
 * KSP 代码生成演示
 * 展示如何使用自动生成的页面注册器
 */
fun main() {
    println("=== KSP 代码生成演示 ===")
    println("这个演示使用了真正的 KSP 处理器生成的代码")
    println()
    
    // 演示基本功能
    demonstrateBasicFeatures()
    
    println()
    
    // 演示高级功能
    demonstrateAdvancedFeatures()
    
    println()
    println("✅ 成功使用了 KSP 生成的 PageRegistry!")
}

/**
 * 演示基本功能
 */
private fun demonstrateBasicFeatures() {
    println("=== KSP 生成的 PageRegistry 演示 ===")
    println()
    
    // 1. 获取所有页面名称
    val pageNames = PageRegistry.getAllPageNames()
    println("所有页面: ${pageNames.joinToString(", ")}")
    println()
    
    // 2. 按优先级排序显示页面
    println("按优先级排序的页面:")
    PageRegistry.getPagesByPriority().forEach { metadata ->
        println("  - ${metadata.name}: ${metadata.description} (优先级: ${metadata.priority})")
    }
    println()
    
    // 3. 创建和使用页面
    val pageName = "home"
    println("尝试创建页面: $pageName")
    val page = PageRegistry.createPage(pageName)
    
    when (page) {
        is HomePage -> {
            page.showHome()
            println("页面信息: ${page.getPageInfo()}")
        }
        else -> println("未知页面类型: $page")
    }
}

/**
 * 演示高级功能
 */
private fun demonstrateAdvancedFeatures() {
    println("=== 高级功能演示 ===")
    println()
    
    // 1. 路由查找
    val route = "/about"
    val pageNameByRoute = PageRegistry.findPageByRoute(route)
    println("路由 '$route' 对应的页面: $pageNameByRoute")
    
    if (pageNameByRoute != null) {
        val page = PageRegistry.createPage(pageNameByRoute)
        when (page) {
            is AboutPage -> page.showAbout()
        }
    }
    println()
    
    // 2. 元数据查询
    println("所有页面的详细信息:")
    PageRegistry.getAllPageMetadata().forEach { metadata ->
        println("  页面: ${metadata.name}")
        println("    路由: ${metadata.route}")
        println("    描述: ${metadata.description}")
        println("    类名: ${metadata.className}")
        println("    优先级: ${metadata.priority}")
        println()
    }
    
    // 3. 动态页面创建
    println("动态创建所有页面:")
    PageRegistry.getAllPageNames().forEach { name ->
        val page = PageRegistry.createPage(name)
        println("  创建页面 '$name': ${page?.javaClass?.simpleName}")
    }
}

第六步:构建和运行

构建项目

代码语言:bash
复制
# 1. 清理项目
./gradlew :modules:kotlin-metaprogramming:clean

# 2. 运行 KSP 处理器(会自动执行)
./gradlew :modules:kotlin-metaprogramming:kspKotlin --info

# 3. 编译并运行演示
./gradlew :modules:kotlin-metaprogramming:run

查看生成的代码

代码语言:bash
复制
# 查看生成的 PageRegistry
cat modules/kotlin-metaprogramming/build/generated/ksp/main/kotlin/generated/PageRegistry.kt

运行结果

代码语言:bash
复制
=== KSP 代码生成演示 ===
这个演示使用了真正的 KSP 处理器生成的代码

=== KSP 生成的 PageRegistry 演示 ===

所有页面: about, contact, home

按优先级排序的页面:
  - home: 应用首页 (优先级: 100)
  - about: 关于我们页面 (优先级: 50)
  - contact: 联系我们页面 (优先级: 30)

尝试创建页面: home
=== 欢迎来到首页 ===
这里是应用的主页面
提供应用的主要功能入口
页面信息: HomePage - 应用首页

=== 高级功能演示 ===

路由 '/about' 对应的页面: about
=== 关于我们 ===
这是一个 Kotlin 元编程学习项目
展示 KSP 代码生成的强大功能

所有页面的详细信息:
  页面: home
    路由: /
    描述: 应用首页
    类名: pages.HomePage
    优先级: 100

  页面: about
    路由: /about
    描述: 关于我们页面
    类名: pages.AboutPage
    优先级: 50

  页面: contact
    路由: /contact
    描述: 联系我们页面
    类名: pages.ContactPage
    优先级: 30

动态创建所有页面:
  创建页面 'about': AboutPage
  创建页面 'contact': ContactPage
  创建页面 'home': HomePage

✅ 成功使用了 KSP 生成的 PageRegistry!

技术深入:KSP 工作原理

编译流程

代码语言:bash
复制
1. 源码解析    → Kotlin 编译器解析源码,构建 AST
2. 符号处理    → KSP 扫描符号,查找注解
3. 代码生成    → 处理器生成新的 Kotlin 文件
4. 增量编译    → 编译器编译原始代码 + 生成代码
5. 字节码输出  → 生成最终的 .class 文件

符号处理过程

代码语言:kotlin
复制
// KSP 的核心处理逻辑
override fun process(resolver: Resolver): List<KSAnnotated> {
    // 1. 查找符号 - 类似 AST 遍历
    val symbols = resolver.getSymbolsWithAnnotation("annotations.Page")
    
    // 2. 验证符号 - 确保符号有效
    val validSymbols = symbols.filter { it.validate() }
    
    // 3. 提取信息 - 从注解中获取元数据
    val pageInfos = validSymbols.map { extractPageInfo(it) }
    
    // 4. 生成代码 - 基于信息生成新文件
    generateCode(pageInfos)
    
    // 5. 返回无效符号 - 用于下一轮处理
    return symbols.filter { !it.validate() }.toList()
}

增量编译支持

KSP 支持增量编译,只处理变更的文件:

代码语言:kotlin
复制
// 正确设置文件依赖关系
val file = codeGenerator.createNewFile(
    dependencies = Dependencies(
        false, // aggregating = false,支持增量编译
        *pageClasses.map { it.containingFile!! }.toTypedArray()
    ),
    packageName = "generated",
    fileName = "PageRegistry"
)

实际应用场景

1. Web 框架路由系统

代码语言:kotlin
复制
@Route("/api/users")
class UserController {
    @GET("/")
    fun getUsers(): List<User> = userService.findAll()
    
    @POST("/")
    fun createUser(@Body user: User): User = userService.save(user)
}

// KSP 生成路由注册器
object RouteRegistry {
    val routes = mapOf(
        "/api/users" to UserController::class,
        // ... 其他路由
    )
}

2. 依赖注入容器

代码语言:kotlin
复制
@Service
class UserService(private val userRepository: UserRepository)

@Repository
class UserRepository

// KSP 生成依赖注入容器
object DIContainer {
    fun <T : Any> getInstance(clazz: KClass<T>): T {
        // 自动生成的实例创建逻辑
    }
}

3. 数据库 ORM 映射

代码语言:kotlin
复制
@Entity("users")
data class User(
    @Id val id: Long,
    @Column("user_name") val name: String,
    @Column val email: String
)

// KSP 生成 SQL 查询构建器
object UserQueries {
    fun findById(id: Long): String = "SELECT * FROM users WHERE id = ?"
    fun findByName(name: String): String = "SELECT * FROM users WHERE user_name = ?"
}

性能优化和最佳实践

1. 处理器性能优化

代码语言:kotlin
复制
class OptimizedPageProcessor : SymbolProcessor {
    // 缓存已处理的符号,避免重复处理
    private val processedSymbols = mutableSetOf<String>()
    
    override fun process(resolver: Resolver): List<KSAnnotated> {
        val symbols = resolver.getSymbolsWithAnnotation("annotations.Page")
        
        // 只处理新的符号
        val newSymbols = symbols.filter { symbol ->
            val key = "${symbol.packageName}.${symbol.simpleName}"
            !processedSymbols.contains(key)
        }
        
        if (newSymbols.isEmpty()) {
            return emptyList()
        }
        
        // 处理新符号
        processSymbols(newSymbols)
        
        // 记录已处理的符号
        newSymbols.forEach { symbol ->
            val key = "${symbol.packageName}.${symbol.simpleName}"
            processedSymbols.add(key)
        }
        
        return emptyList()
    }
}

2. 代码生成优化

代码语言:kotlin
复制
private fun generateOptimizedCode(pageInfos: List<PageInfo>): String {
    return buildString {
        // 使用 StringBuilder 而不是字符串拼接
        // 预分配合适的容量
        ensureCapacity(pageInfos.size * 200)
        
        // 批量生成,减少 I/O 操作
        appendLine("object PageRegistry {")
        
        // 使用更高效的数据结构
        appendLine("    private val pageFactories = hashMapOf<String, () -> Any>(")
        pageInfos.forEach { info ->
            appendLine("        \"${info.name}\" to { ${info.className}() },")
        }
        appendLine("    )")
        
        appendLine("}")
    }
}

3. 错误处理和调试

代码语言:kotlin
复制
class RobustPageProcessor : SymbolProcessor {
    override fun process(resolver: Resolver): List<KSAnnotated> {
        return try {
            processInternal(resolver)
        } catch (e: Exception) {
            logger.error("处理器执行失败: ${e.message}", e)
            // 返回空列表,避免编译失败
            emptyList()
        }
    }
    
    private fun processInternal(resolver: Resolver): List<KSAnnotated> {
        val symbols = resolver.getSymbolsWithAnnotation("annotations.Page")
        
        symbols.forEach { symbol ->
            try {
                validateSymbol(symbol)
            } catch (e: Exception) {
                logger.warn("符号验证失败: ${symbol.simpleName}, 错误: ${e.message}")
            }
        }
        
        // ... 处理逻辑
        return emptyList()
    }
    
    private fun validateSymbol(symbol: KSAnnotated) {
        if (symbol !is KSClassDeclaration) {
            throw IllegalArgumentException("@Page 只能用于类")
        }
        
        if (symbol.isAbstract()) {
            throw IllegalArgumentException("@Page 不能用于抽象类")
        }
        
        // 检查是否有无参构造函数
        val hasNoArgConstructor = symbol.primaryConstructor?.parameters?.isEmpty() == true
        if (!hasNoArgConstructor) {
            logger.warn("${symbol.simpleName} 没有无参构造函数,可能无法正确实例化")
        }
    }
}

扩展和进阶

1. 支持更复杂的注解

代码语言:kotlin
复制
@Page(
    name = "user-profile",
    route = "/user/{id}",
    permissions = ["USER_READ", "PROFILE_VIEW"],
    cacheStrategy = CacheStrategy.MEMORY,
    dependencies = [UserService::class, ProfileService::class]
)
class UserProfilePage

2. 多文件生成

代码语言:kotlin
复制
class AdvancedPageProcessor : SymbolProcessor {
    override fun process(resolver: Resolver): List<KSAnnotated> {
        val pageClasses = getPageClasses(resolver)
        
        // 生成多个文件
        generatePageRegistry(pageClasses)      // 页面注册器
        generateRouteMapping(pageClasses)      // 路由映射
        generatePermissionCheck(pageClasses)   // 权限检查
        generateDocumentation(pageClasses)     // API 文档
        
        return emptyList()
    }
}

3. 与 Gradle 插件集成

代码语言:kotlin
复制
// 自定义 Gradle 插件
class PageGeneratorPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        project.extensions.create("pageGenerator", PageGeneratorExtension::class.java)
        
        project.tasks.register("generatePages") { task ->
            task.doLast {
                // 执行页面生成逻辑
            }
        }
    }
}

总结

通过最近四篇文章的学习,我们已经掌握了从注解定义到代码生成的完整技术栈,可以在实际项目中应用这些强大的技术来提升开发效率和代码质量。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前置知识回顾
    • 第一部分:元编程基础
    • 第二部分:反射深入
    • 第三部分:注解系统
  • 什么是 KSP?
    • KSP 简介
    • 前端开发者的类比
    • KSP vs 其他技术
  • 项目需求分析
    • 业务场景
    • 解决方案
    • 技术架构
  • 第一步:定义页面注解
    • 设计要点
  • 第二步:创建示例页面
    • 页面设计特点
  • 第三步:构建 KSP 处理器
    • 创建处理器子项目
    • 实现 KSP 处理器
    • 注册处理器
  • 第四步:配置构建系统
    • 主项目配置
    • 项目设置
  • 第五步:使用生成的代码
  • 第六步:构建和运行
    • 构建项目
    • 查看生成的代码
    • 运行结果
  • 技术深入:KSP 工作原理
    • 编译流程
    • 符号处理过程
    • 增量编译支持
  • 实际应用场景
    • 1. Web 框架路由系统
    • 2. 依赖注入容器
    • 3. 数据库 ORM 映射
  • 性能优化和最佳实践
    • 1. 处理器性能优化
    • 2. 代码生成优化
    • 3. 错误处理和调试
  • 扩展和进阶
    • 1. 支持更复杂的注解
    • 2. 多文件生成
    • 3. 与 Gradle 插件集成
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档