如何使用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
相关产品推荐
相关产品推荐

