You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Kotlin Symbol Processing(KSP)为函数自动新增指定参数

先确认你的AddHeader注解配置正确,建议指定作用域为函数、保留期为源码级:

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.SOURCE)
annotation class AddHeader
实现方案

整体思路是通过KSP扫描所有标注了AddHeader的Retrofit服务接口函数,生成修改后的新接口,自动插入带@retrofit2.http.Header注解的参数,同时移除原函数上的AddHeader注解,具体实现步骤如下:

1. 项目环境配置

首先在项目根目录build.gradle.kts中添加KSP插件依赖:

plugins {
    id("com.google.devtools.ksp") version "1.9.20-1.0.14" apply false
}

在注解处理模块的build.gradle.kts中引入相关依赖:

plugins {
    kotlin("jvm")
    id("com.google.devtools.ksp")
}

dependencies {
    implementation(kotlin("stdlib-jdk8"))
    // KSP核心API
    implementation("com.google.devtools.ksp:symbol-processing-api:1.9.20-1.0.14")
    // 代码生成工具KotlinPoet
    implementation("com.squareup:kotlinpoet:1.16.0")
    implementation("com.squareup:kotlinpoet-ksp:1.16.0")
    // Retrofit依赖,用于识别Retrofit相关注解
    implementation("com.squareup.retrofit2:retrofit:2.9.0")
}

在业务模块的build.gradle.kts中配置KSP生成代码的源目录,避免Retrofit无法识别生成的接口:

kotlin {
    sourceSets.main {
        kotlin.srcDir("build/generated/ksp/main/kotlin")
    }
}

2. 自定义KSP符号处理器

实现符号处理器扫描目标注解,生成修改后的接口代码:

class AddHeaderProcessor(
    private val codeGenerator: CodeGenerator,
    private val logger: KSPLogger
) : SymbolProcessor {

    override fun process(resolver: Resolver): List<KSAnnotated> {
        // 筛选所有标注了@AddHeader的函数
        val targetFunctions = resolver.getSymbolsWithAnnotation(AddHeader::class.qualifiedName!!)
            .filterIsInstance<KSFunctionDeclaration>()
            .toList()

        if (targetFunctions.isEmpty()) return emptyList()

        // 按所属接口分组处理
        targetFunctions.groupBy { it.parentDeclaration as KSClassDeclaration }
            .forEach { (originInterface, functions) ->
                generateModifiedInterface(originInterface, functions)
            }

        return emptyList()
    }

    private fun generateModifiedInterface(
        originInterface: KSClassDeclaration,
        targetFunctions: List<KSFunctionDeclaration>
    ) {
        val packageName = originInterface.packageName.asString()
        val interfaceName = originInterface.simpleName.asString()

        val interfaceBuilder = TypeSpec.interfaceBuilder(interfaceName)
            .addModifiers(KModifier.PUBLIC)

        // 遍历原接口所有函数
        originInterface.getAllFunctions().forEach { func ->
            val isTarget = targetFunctions.any { it.simpleName == func.simpleName }
            val funcBuilder = FunSpec.builder(func.simpleName.asString())
                .addModifiers(KModifier.PUBLIC, KModifier.ABSTRACT)
                .returns(func.returnType.resolve().toTypeName())

            // 目标函数优先插入Header参数
            if (isTarget) {
                val headerParam = ParameterSpec.builder("header", String::class)
                    .addAnnotation(
                        AnnotationSpec.builder(ClassName("retrofit2.http", "Header"))
                            .addMember("%S", "custom_key")
                            .build()
                    )
                    .defaultValue("%S", "")
                    .build()
                funcBuilder.addParameter(headerParam)
            }

            // 复制原函数所有参数
            func.parameters.forEach { param ->
                val paramSpec = ParameterSpec.builder(
                    param.name!!.asString(),
                    param.type.resolve().toTypeName()
                ).apply {
                    param.annotations.forEach { addAnnotation(it.toAnnotationSpec()) }
                    if (param.hasDefaultValue()) defaultValue(param.getDefaultValueExpression()!!.text)
                }.build()
                funcBuilder.addParameter(paramSpec)
            }

            // 复制原函数除@AddHeader外的所有注解
            func.annotations.filter {
                it.annotationType.resolve().declaration.qualifiedName?.asString() != AddHeader::class.qualifiedName
            }.forEach { funcBuilder.addAnnotation(it.toAnnotationSpec()) }

            // 兼容suspend函数
            if (func.modifiers.contains(KModifier.SUSPEND)) {
                funcBuilder.addModifiers(KModifier.SUSPEND)
            }

            interfaceBuilder.addFunction(funcBuilder.build())
        }

        // 生成代码文件
        FileSpec.builder(packageName, interfaceName)
            .addType(interfaceBuilder.build())
            .build()
            .writeTo(codeGenerator, Dependencies(true, originInterface.containingFile!!))
    }
}

3. 注册处理器

创建AddHeaderProcessorProvider用于实例化处理器:

class AddHeaderProcessorProvider : SymbolProcessorProvider {
    override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
        return AddHeaderProcessor(environment.codeGenerator, environment.logger)
    }
}

在处理器模块的src/main/resources/META-INF/services目录下新建文件com.google.devtools.ksp.processing.SymbolProcessorProvider,文件内容填入你实现的AddHeaderProcessorProvider的全限定类名。

注意:如果需要直接替换原接口而非生成同名新接口,需要将原接口定义放入单独的资源目录,配置KSP的输出路径覆盖原类编译路径,避免类重复冲突。也可以调整生成的接口命名规则,业务代码直接引用生成后的接口即可。

内容的提问来源于stack exchange,提问作者xiaobo0113

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 09:06:01