Kotlin注解案例详解
我将通过多个实际案例深入讲解Kotlin注解的用法,包括自定义注解、注解处理器(KAPT/KSP)、运行时注解处理等。
案例1:自定义注解与运行时处理
1.1 数据验证注解
// ================ 验证注解定义 ================
/**
* 验证注解的元注解
*/
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class Validate
/**
* 非空验证
*/
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class NotEmpty(
val message: String = "字段不能为空"
)
/**
* 长度验证
*/
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class Length(
val min: Int = 0,
val max: Int = Int.MAX_VALUE,
val message: String = "长度不符合要求"
)
/**
* 正则验证
*/
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class Pattern(
val regex: String,
val message: String = "格式不正确"
)
/**
* 范围验证
*/
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class Range(
val min: Long = Long.MIN_VALUE,
val max: Long = Long.MAX_VALUE,
val message: String = "值超出范围"
)
/**
* 邮箱验证
*/
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class Email(
val message: String = "邮箱格式不正确"
)
// ================ 验证器类 ================
/**
* 验证结果
*/
data class ValidationResult(
val isValid: Boolean,
val errors: Map<String, List<String>>
)
/**
* 验证异常
*/
class ValidationException(val validationResult: ValidationResult) :
IllegalArgumentException("数据验证失败: ${validationResult.errors}")
/**
* 验证器
*/
object Validator {
fun validate(obj: Any): ValidationResult {
val errors = mutableMapOf<String, MutableList<String>>()
obj.javaClass.declaredFields.forEach { field ->
// 跳过非验证字段
if (!field.isAnnotationPresent(Validate::class.java)) {
return@forEach
}
field.isAccessible = true
val value = field.get(obj)
val fieldName = field.name
// 验证NotEmpty
field.getAnnotation(NotEmpty::class.java)?.let { annotation ->
if (value == null || (value is String && value.isBlank())) {
errors.getOrPut(fieldName) { mutableListOf() }
.add(annotation.message)
}
}
// 验证Length
field.getAnnotation(Length::class.java)?.let { annotation ->
when (value) {
is String -> {
if (value.length < annotation.min || value.length > annotation.max) {
errors.getOrPut(fieldName) { mutableListOf() }
.add("${annotation.message} (当前长度: ${value.length}, 要求: ${annotation.min}-${annotation.max})")
}
}
is Collection<*> -> {
if (value.size < annotation.min || value.size > annotation.max) {
errors.getOrPut(fieldName) { mutableListOf() }
.add("${annotation.message} (当前长度: ${value.size}, 要求: ${annotation.min}-${annotation.max})")
}
}
}
}
// 验证Pattern
field.getAnnotation(Pattern::class.java)?.let { annotation ->
if (value is String && !Regex(annotation.regex).matches(value)) {
errors.getOrPut(fieldName) { mutableListOf() }
.add(annotation.message)
}
}
// 验证Range
field.getAnnotation(Range::class.java)?.let { annotation ->
when (value) {
is Int -> {
if (value < annotation.min || value > annotation.max) {
errors.getOrPut(fieldName) { mutableListOf() }
.add("${annotation.message} (当前值: $value, 要求: ${annotation.min}-${annotation.max})")
}
}
is Long -> {
if (value < annotation.min || value > annotation.max) {
errors.getOrPut(fieldName) { mutableListOf() }
.add("${annotation.message} (当前值: $value, 要求: ${annotation.min}-${annotation.max})")
}
}
}
}
// 验证Email
field.getAnnotation(Email::class.java)?.let { annotation ->
if (value is String) {
val emailRegex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\$"
if (!Regex(emailRegex).matches(value)) {
errors.getOrPut(fieldName) { mutableListOf() }
.add(annotation.message)
}
}
}
}
return ValidationResult(errors.isEmpty(), errors)
}
fun validateAndThrow(obj: Any) {
val result = validate(obj)
if (!result.isValid) {
throw ValidationException(result)
}
}
}
// ================ 使用示例 ================
data class User(
@field:Validate
@field:NotEmpty("用户名不能为空")
@field:Length(min = 3, max = 20, message = "用户名长度必须为3-20位")
val username: String,
@field:Validate
@field:NotEmpty("密码不能为空")
@field:Length(min = 6, max = 30, message = "密码长度必须为6-30位")
val password: String,
@field:Validate
@field:Email("邮箱格式不正确")
val email: String?,
@field:Validate
@field:Range(min = 0, max = 150, message = "年龄必须在0-150之间")
val age: Int,
@field:Validate
@field:Pattern(
regex = "^1[3-9]\\d{9}\$",
message = "手机号格式不正确"
)
val phone: String?,
// 非验证字段
val createdAt: Long = System.currentTimeMillis()
)
fun main() {
val validUser = User(
username = "john_doe",
password = "password123",
email = "john@example.com",
age = 25,
phone = "13800138000"
)
val invalidUser = User(
username = "ab", // 太短
password = "123", // 太短
email = "invalid-email",
age = 200, // 超出范围
phone = "123456" // 格式错误
)
println("验证有效用户:")
println(Validator.validate(validUser))
println("\n验证无效用户:")
println(Validator.validate(invalidUser))
// 验证并抛出异常
try {
Validator.validateAndThrow(invalidUser)
} catch (e: ValidationException) {
println("\n验证异常: ${e.validationResult.errors}")
}
}
案例2:依赖注入注解
// ================ DI注解定义 ================
/**
* 标记可注入的类
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class Component
/**
* 单例组件
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@Component
annotation class Singleton
/**
* 配置类
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@Component
annotation class Configuration
/**
* Bean定义
*/
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class Bean
/**
* 自动注入
*/
@Target(AnnotationTarget.FIELD, AnnotationTarget.CONSTRUCTOR)
@Retention(AnnotationRetention.RUNTIME)
annotation class Autowired
/**
* 限定符
*/
@Target(AnnotationTarget.FIELD, AnnotationTarget.FUNCTION, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
annotation class Qualifier(val name: String)
/**
* 值注入
*/
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class Value(val expression: String)
// ================ DI容器实现 ================
/**
* Bean定义
*/
data class BeanDefinition(
val name: String,
val type: Class<*>,
val instance: Any? = null,
val isSingleton: Boolean = false,
val factory: (() -> Any)? = null
)
/**
* 简易DI容器
*/
class ApplicationContext(private val scanPackages: List<String> = emptyList()) {
private val beanDefinitions = mutableMapOf<String, BeanDefinition>()
private val singletonInstances = mutableMapOf<String, Any>()
init {
if (scanPackages.isNotEmpty()) {
scanComponents()
}
}
/**
* 扫描组件
*/
private fun scanComponents() {
// 这里简化实现,实际项目会使用类路径扫描
println("扫描组件包: $scanPackages")
}
/**
* 注册Bean
*/
fun <T : Any> registerBean(
name: String,
type: Class<T>,
instance: T? = null,
isSingleton: Boolean = false,
factory: (() -> T)? = null
) {
beanDefinitions[name] = BeanDefinition(name, type, instance, isSingleton, factory)
}
/**
* 获取Bean
*/
@Suppress("UNCHECKED_CAST")
fun <T : Any> getBean(name: String): T {
val definition = beanDefinitions[name]
?: throw NoSuchElementException("未找到Bean: $name")
return when {
definition.isSingleton -> {
singletonInstances.getOrPut(name) {
createBeanInstance(definition)
} as T
}
definition.instance != null -> definition.instance as T
definition.factory != null -> definition.factory!!() as T
else -> createBeanInstance(definition) as T
}
}
/**
* 按类型获取Bean
*/
@Suppress("UNCHECKED_CAST")
fun <T : Any> getBean(type: Class<T>): T {
val matchingBeans = beanDefinitions.values
.filter { type.isAssignableFrom(it.type) }
return when (matchingBeans.size) {
0 -> throw NoSuchElementException("未找到类型为 ${type.name} 的Bean")
1 -> getBean(matchingBeans.first().name) as T
else -> throw IllegalStateException("找到多个类型为 ${type.name} 的Bean")
}
}
/**
* 创建Bean实例
*/
private fun createBeanInstance(definition: BeanDefinition): Any {
val constructors = definition.type.declaredConstructors
// 查找有@Autowired注解的构造器
val autowiredConstructor = constructors.find { constructor ->
constructor.isAnnotationPresent(Autowired::class.java)
}
return if (autowiredConstructor != null) {
// 获取构造器参数
val parameters = autowiredConstructor.parameters.map { param ->
if (param.isAnnotationPresent(Qualifier::class.java)) {
val qualifier = param.getAnnotation(Qualifier::class.java)
getBean(qualifier.name)
} else {
getBean(param.type)
}
}.toTypedArray()
autowiredConstructor.newInstance(*parameters)
} else {
// 使用默认构造器
definition.type.getDeclaredConstructor().newInstance()
}.also { instance ->
// 注入字段
injectFields(instance)
}
}
/**
* 注入字段
*/
private fun injectFields(instance: Any) {
instance.javaClass.declaredFields.forEach { field ->
if (field.isAnnotationPresent(Autowired::class.java)) {
field.isAccessible = true
val beanToInject = if (field.isAnnotationPresent(Qualifier::class.java)) {
val qualifier = field.getAnnotation(Qualifier::class.java)
getBean(qualifier.name)
} else {
getBean(field.type)
}
field.set(instance, beanToInject)
}
// 注入值
if (field.isAnnotationPresent(Value::class.java)) {
field.isAccessible = true
val valueAnnotation = field.getAnnotation(Value::class.java)
val value = resolveValueExpression(valueAnnotation.expression)
field.set(instance, convertValue(value, field.type))
}
}
}
/**
* 解析值表达式
*/
private fun resolveValueExpression(expression: String): String {
// 简化实现,实际会从配置文件读取
return when (expression) {
"\${app.name}" -> "MyApplication"
"\${app.version}" -> "1.0.0"
"\${database.url}" -> "jdbc:mysql://localhost:3306/test"
else -> expression
}
}
/**
* 转换值类型
*/
private fun convertValue(value: String, targetType: Class<*>): Any {
return when (targetType) {
String::class.java -> value
Int::class.java -> value.toInt()
Long::class.java -> value.toLong()
Boolean::class.java -> value.toBoolean()
else -> throw IllegalArgumentException("不支持的转换类型: ${targetType.name}")
}
}
}
// ================ 使用示例 ================
// 定义Repository
interface UserRepository {
fun findById(id: Long): String
fun save(user: String): Long
}
@Singleton
@Qualifier("userRepository")
class UserRepositoryImpl : UserRepository {
override fun findById(id: Long): String {
return "User_$id"
}
override fun save(user: String): Long {
println("保存用户: $user")
return System.currentTimeMillis()
}
}
@Singleton
@Qualifier("productRepository")
class ProductRepositoryImpl : UserRepository {
override fun findById(id: Long): String {
return "Product_$id"
}
override fun save(user: String): Long {
println("保存产品: $user")
return System.currentTimeMillis()
}
}
// 定义Service
interface UserService {
fun getUser(id: Long): String
}
@Singleton
class UserServiceImpl : UserService {
@Autowired
@Qualifier("userRepository")
private lateinit var userRepository: UserRepository
@Value("\${app.name}")
private lateinit var appName: String
@Value("\${app.version}")
private lateinit var appVersion: String
override fun getUser(id: Long): String {
println("应用: $appName v$appVersion")
return userRepository.findById(id)
}
}
// 定义Controller
@Singleton
class UserController {
@Autowired
private lateinit var userService: UserService
fun handleRequest(id: Long): String {
return userService.getUser(id)
}
}
// 配置类
@Configuration
class AppConfig {
@Bean
fun configBean(): Map<String, String> {
return mapOf(
"environment" to "development",
"maxConnections" to "100"
)
}
}
fun main() {
// 创建DI容器
val context = ApplicationContext(listOf("com.example"))
// 注册Bean
context.registerBean("userRepository", UserRepositoryImpl::class.java, isSingleton = true)
context.registerBean("productRepository", ProductRepositoryImpl::class.java, isSingleton = true)
context.registerBean("userService", UserServiceImpl::class.java, isSingleton = true)
context.registerBean("userController", UserController::class.java, isSingleton = true)
// 获取Bean并使用
val controller = context.getBean<UserController>("userController")
val result = controller.handleRequest(1)
println("结果: $result")
// 按类型获取Bean
val service = context.getBean(UserService::class.java)
println("按类型获取Bean: ${service.getUser(2)}")
}
案例3:编译时注解处理器(KAPT)
项目结构:
build.gradle.kts (注解处理器模块)
build.gradle.kts (使用模块)
3.1 注解处理器模块
build.gradle.kts (注解处理器模块):
plugins {
kotlin("jvm")
`maven-publish`
}
dependencies {
implementation(kotlin("stdlib"))
implementation("com.google.auto.service:auto-service:1.0.1")
kapt("com.google.auto.service:auto-service:1.0.1")
implementation("com.squareup:kotlinpoet:1.12.0")
}
sourceSets {
main {
java.srcDirs("src/main/kotlin")
resources.srcDirs("src/main/resources")
}
}
publishing {
publications {
create<MavenPublication>("maven") {
from(components["java"])
}
}
}
注解定义 (src/main/kotlin/com/example/annotations.kt):
package com.example.annotations
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
annotation class Builder
@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.SOURCE)
annotation class BuilderProperty(
val required: Boolean = false,
val defaultValue: String = ""
)
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
annotation class ToString
@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.SOURCE)
annotation class ToStringExclude
注解处理器 (src/main/kotlin/com/example/processor/BuilderProcessor.kt):
package com.example.processor
import com.google.auto.service.AutoService
import com.squareup.kotlinpoet.*
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.example.annotations.Builder
import com.example.annotations.BuilderProperty
import javax.annotation.processing.*
import javax.lang.model.SourceVersion
import javax.lang.model.element.TypeElement
import javax.lang.model.element.Element
import javax.lang.model.element.ElementKind
import javax.tools.Diagnostic
@AutoService(Processor::class)
class BuilderProcessor : AbstractProcessor() {
private lateinit var filer: Filer
private lateinit var messager: Messager
override fun init(processingEnv: ProcessingEnvironment) {
super.init(processingEnv)
filer = processingEnv.filer
messager = processingEnv.messager
}
override fun getSupportedAnnotationTypes(): Set<String> {
return setOf(Builder::class.java.canonicalName)
}
override fun getSupportedSourceVersion(): SourceVersion {
return SourceVersion.latestSupported()
}
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
roundEnv.getElementsAnnotatedWith(Builder::class.java).forEach { element ->
if (element.kind != ElementKind.CLASS) {
messager.printMessage(Diagnostic.Kind.ERROR, "@Builder只能用于类", element)
return@forEach
}
val classElement = element as TypeElement
generateBuilderClass(classElement)
}
return true
}
private fun generateBuilderClass(classElement: TypeElement) {
val className = classElement.simpleName.toString()
val packageName = processingEnv.elementUtils.getPackageOf(classElement).toString()
val builderClassName = "${className}Builder"
val typeSpec = TypeSpec.classBuilder(builderClassName)
.addModifiers(KModifier.PUBLIC)
// 添加属性字段
classElement.enclosedElements
.filter { it.kind == ElementKind.FIELD }
.forEach { field ->
val fieldName = field.simpleName.toString()
val fieldType = field.asType().toString()
val builderProperty = field.getAnnotation(BuilderProperty::class.java)
val propertySpec = PropertySpec.builder(fieldName, fieldType.asTypeName())
.mutable(true)
.initializer(
if (builderProperty?.required == true) {
"error(\"$fieldName是必填字段\")"
} else if (builderProperty?.defaultValue?.isNotEmpty() == true) {
"\"${builderProperty.defaultValue}\""
} else {
when (fieldType) {
"kotlin.String" -> "\"\""
"kotlin.Int" -> "0"
"kotlin.Long" -> "0L"
"kotlin.Boolean" -> "false"
else -> "null"
}
}
)
.build()
typeSpec.addProperty(propertySpec)
// 添加setter方法
val setterFun = FunSpec.builder(fieldName)
.addParameter(fieldName, fieldType.asTypeName())
.returns(ClassName(packageName, builderClassName))
.addStatement("this.$fieldName = $fieldName")
.addStatement("return this")
.build()
typeSpec.addFunction(setterFun)
}
// 添加build方法
val buildFun = FunSpec.builder("build")
.returns(ClassName(packageName, className))
.addStatement("return $className(")
.apply {
classElement.enclosedElements
.filter { it.kind == ElementKind.FIELD }
.forEachIndexed { index, field ->
val fieldName = field.simpleName.toString()
val comma = if (index > 0) "," else ""
addStatement(" ${comma}$fieldName = $fieldName")
}
}
.addStatement(")")
.build()
typeSpec.addFunction(buildFun)
// 生成文件
val fileSpec = FileSpec.builder(packageName, builderClassName)
.addType(typeSpec.build())
.build()
try {
fileSpec.writeTo(filer)
messager.printMessage(Diagnostic.Kind.NOTE, "生成Builder类: $builderClassName")
} catch (e: Exception) {
messager.printMessage(Diagnostic.Kind.ERROR, "生成失败: ${e.message}")
}
}
}
// 扩展函数:转换类型字符串为TypeName
private fun String.asTypeName(): TypeName {
return when (this) {
"kotlin.String" -> STRING
"kotlin.Int" -> INT
"kotlin.Long" -> LONG
"kotlin.Boolean" -> BOOLEAN
"kotlin.Double" -> DOUBLE
"kotlin.Float" -> FLOAT
"kotlin.Byte" -> BYTE
"kotlin.Short" -> SHORT
"kotlin.Char" -> CHAR
"kotlin.Unit" -> UNIT
"kotlin.Any" -> ANY
else -> {
// 处理泛型类型,如List<String>
if (contains("<")) {
val rawType = substringBefore("<")
val typeArg = substringAfter("<").substringBefore(">")
val rawTypeName = ClassName.bestGuess(rawType)
val typeArgName = typeArg.asTypeName()
rawTypeName.parameterizedBy(typeArgName)
} else {
ClassName.bestGuess(this)
}
}
}
}
3.2 使用模块
build.gradle.kts (使用模块):
plugins {
kotlin("jvm")
kotlin("kapt")
}
dependencies {
implementation(project(":annotations")) // 注解模块
kapt(project(":processor")) // 注解处理器模块
}
kapt {
correctErrorTypes = true
}
实体类 (src/main/kotlin/com/example/model/User.kt):
package com.example.model
import com.example.annotations.Builder
import com.example.annotations.BuilderProperty
@Builder
data class User(
@BuilderProperty(required = true)
val id: Long,
@BuilderProperty(required = true)
val username: String,
@BuilderProperty(defaultValue = "")
val email: String,
@BuilderProperty(defaultValue = "0")
val age: Int,
@BuilderProperty(defaultValue = "false")
val isActive: Boolean
)
编译后会生成 (build/generated/source/kapt/main/com/example/model/UserBuilder.kt):
package com.example.model
import kotlin.String
import kotlin.Unit
import kotlin.error
public class UserBuilder {
public var id: Long = error("id是必填字段")
public var username: String = error("username是必填字段")
public var email: String = ""
public var age: Int = 0
public var isActive: Boolean = false
public fun id(id: Long): UserBuilder {
this.id = id
return this
}
public fun username(username: String): UserBuilder {
this.username = username
return this
}
public fun email(email: String): UserBuilder {
this.email = email
return this
}
public fun age(age: Int): UserBuilder {
this.age = age
return this
}
public fun isActive(isActive: Boolean): UserBuilder {
this.isActive = isActive
return this
}
public fun build(): User = User(
id = id,
username = username,
email = email,
age = age,
isActive = isActive
)
}
使用生成的Builder:
fun main() {
val user = UserBuilder()
.id(1L)
.username("john_doe")
.email("john@example.com")
.age(25)
.isActive(true)
.build()
println(user)
}
案例4:Kotlin Symbol Processing (KSP) - 更现代的注解处理器
4.1 KSP处理器实现
build.gradle.kts (KSP处理器模块):
plugins {
kotlin("jvm")
`maven-publish`
}
dependencies {
implementation(kotlin("stdlib"))
implementation("com.google.devtools.ksp:symbol-processing-api:1.8.0-1.0.9")
implementation("com.squareup:kotlinpoet:1.12.0")
implementation("com.squareup:kotlinpoet-ksp:1.12.0")
}
KSP处理器 (src/main/kotlin/com/example/ksp/ToStringProcessor.kt):
package com.example.ksp
import com.google.devtools.ksp.processing.*
import com.google.devtools.ksp.symbol.*
import com.google.devtools.ksp.validate
import com.squareup.kotlinpoet.*
import com.squareup.kotlinpoet.ksp.toClassName
import com.squareup.kotlinpoet.ksp.toTypeName
import com.example.annotations.ToString
import com.example.annotations.ToStringExclude
class ToStringProcessor(
private val codeGenerator: CodeGenerator,
private val logger: KSPLogger
) : SymbolProcessor {
override fun process(resolver: Resolver): List<KSAnnotated> {
val symbols = resolver.getSymbolsWithAnnotation(ToString::class.qualifiedName!!)
val ret = symbols.filter { !it.validate() }.toList()
symbols
.filter { it is KSClassDeclaration && it.validate() }
.forEach { it.accept(ToStringVisitor(), Unit) }
return ret
}
inner class ToStringVisitor : KSVisitorVoid() {
override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
val packageName = classDeclaration.packageName.asString()
val className = classDeclaration.simpleName.asString()
val generatedClassName = "${className}Extensions"
logger.info("处理类: $className")
// 获取所有属性(排除排除的属性)
val properties = classDeclaration.getAllProperties()
.filter { property ->
property.annotations.none {
it.annotationType.resolve().declaration.qualifiedName?.asString() ==
ToStringExclude::class.qualifiedName
}
}
.toList()
// 生成toString扩展函数
val toStringFun = FunSpec.builder("toStringFormatted")
.receiver(classDeclaration.toClassName())
.returns(String::class)
.addStatement("return buildString {")
.addStatement(" append(\"$className(\")")
properties.forEachIndexed { index, property ->
val propertyName = property.simpleName.asString()
val comma = if (index > 0) ", " else ""
toStringFun.addStatement(
" append(\"$comma$propertyName = \${this.$propertyName}\")"
)
}
toStringFun.addStatement(" append(\")\")")
toStringFun.addStatement("}")
// 生成文件
val fileSpec = FileSpec.builder(packageName, generatedClassName)
.addFunction(toStringFun.build())
.build()
val dependencies = Dependencies(
aggregating = false,
classDeclaration.containingFile!!,
*resolver.getAllFiles().toList().toTypedArray()
)
codeGenerator.createNewFile(
dependencies,
packageName,
generatedClassName
).use { outputStream ->
fileSpec.writeTo(outputStream)
}
}
}
}
class ToStringProcessorProvider : SymbolProcessorProvider {
override fun create(
environment: SymbolProcessorEnvironment
): SymbolProcessor {
return ToStringProcessor(environment.codeGenerator, environment.logger)
}
}
4.2 配置KSP (使用模块的build.gradle.kts):
plugins {
kotlin("jvm")
id("com.google.devtools.ksp")
}
dependencies {
implementation(project(":annotations"))
ksp(project(":ksp-processor"))
}
kotlin {
sourceSets.main {
kotlin.srcDir("build/generated/ksp/main/kotlin")
}
}
ksp {
arg("option1", "value1")
arg("option2", "value2")
}
案例5:路由注解框架
// ================ 路由注解定义 ================
/**
* Activity路由
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class Router(
val path: String,
val description: String = ""
)
/**
* 方法路由
*/
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class Route(
val path: String,
val method: HttpMethod = HttpMethod.GET,
val authRequired: Boolean = false
)
/**
* 路径参数
*/
@Target(AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
annotation class PathParam(val name: String)
/**
* 查询参数
*/
@Target(AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
annotation class QueryParam(val name: String)
/**
* 请求体
*/
@Target(AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
annotation class Body
/**
* HTTP方法枚举
*/
enum class HttpMethod {
GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS
}
// ================ 路由信息类 ================
/**
* 参数信息
*/
data class ParameterInfo(
val name: String,
val type: Class<*>,
val annotation: Annotation?
)
/**
* 路由信息
*/
data class RouteInfo(
val path: String,
val method: HttpMethod,
val handler: Any,
val function: java.lang.reflect.Method,
val parameters: List<ParameterInfo>,
val authRequired: Boolean
)
/**
* 路由表
*/
object RouterTable {
private val routes = mutableMapOf<String, MutableMap<HttpMethod, RouteInfo>>()
fun register(controller: Any) {
val controllerClass = controller.javaClass
// 获取控制器基础路径
val basePath = controllerClass.getAnnotation(Router::class.java)?.path ?: ""
controllerClass.declaredMethods.forEach { method ->
val routeAnnotation = method.getAnnotation(Route::class.java)
if (routeAnnotation != null) {
val fullPath = if (basePath.isNotEmpty()) {
"$basePath${routeAnnotation.path}"
} else {
routeAnnotation.path
}
// 获取参数信息
val parameters = method.parameters.map { param ->
val annotations = param.annotations
val annotation = annotations.firstOrNull {
it is PathParam || it is QueryParam || it is Body
}
ParameterInfo(param.name, param.type, annotation)
}
val routeInfo = RouteInfo(
path = fullPath,
method = routeAnnotation.method,
handler = controller,
function = method,
parameters = parameters,
authRequired = routeAnnotation.authRequired
)
routes.getOrPut(fullPath) { mutableMapOf() }[routeAnnotation.method] = routeInfo
}
}
}
fun findRoute(path: String, method: HttpMethod): RouteInfo? {
return routes[path]?.get(method)
}
fun getAllRoutes(): List<RouteInfo> {
return routes.values.flatMap { it.values }
}
}
/**
* HTTP请求上下文
*/
data class RequestContext(
val path: String,
val method: HttpMethod,
val headers: Map<String, String>,
val pathParams: Map<String, String>,
val queryParams: Map<String, List<String>>,
val body: String?
)
/**
* HTTP响应
*/
data class HttpResponse(
val statusCode: Int,
val headers: Map<String, String>,
val body: Any?
)
/**
* 路由处理器
*/
class RouterHandler {
fun handleRequest(context: RequestContext): HttpResponse {
val routeInfo = RouterTable.findRoute(context.path, context.method)
?: return HttpResponse(404, emptyMap(), "路由未找到: ${context.path}")
// 检查权限
if (routeInfo.authRequired && !checkAuth(context)) {
return HttpResponse(401, emptyMap(), "未授权")
}
try {
// 准备参数
val args = prepareArguments(routeInfo, context)
// 调用方法
val result = routeInfo.function.invoke(routeInfo.handler, *args)
return HttpResponse(200, emptyMap(), result)
} catch (e: Exception) {
return HttpResponse(500, emptyMap(), "服务器错误: ${e.message}")
}
}
private fun prepareArguments(routeInfo: RouteInfo, context: RequestContext): Array<Any?> {
return routeInfo.parameters.map { param ->
when (val annotation = param.annotation) {
is PathParam -> context.pathParams[annotation.name]
is QueryParam -> context.queryParams[annotation.name]?.firstOrNull()
is Body -> parseBody(context.body, param.type)
else -> null
}
}.toTypedArray()
}
private fun parseBody(body: String?, type: Class<*>): Any? {
if (body.isNullOrEmpty()) return null
return when (type) {
String::class.java -> body
Map::class.java -> {
body.split("&").associate { pair ->
val (key, value) = pair.split("=")
key to value
}
}
else -> {
// 这里可以集成JSON解析库
body
}
}
}
private fun checkAuth(context: RequestContext): Boolean {
val authHeader = context.headers["Authorization"]
return authHeader?.startsWith("Bearer ") == true
}
}
// ================ 控制器定义 ================
@Router("/api/users", "用户管理")
class UserController {
@Route("/", HttpMethod.GET)
fun getUsers(
@QueryParam("page") page: Int = 1,
@QueryParam("size") size: Int = 20
): Map<String, Any> {
return mapOf(
"page" to page,
"size" to size,
"users" to listOf(
mapOf("id" to 1, "name" to "张三"),
mapOf("id" to 2, "name" to "李四")
)
)
}
@Route("/{id}", HttpMethod.GET, authRequired = true)
fun getUserById(@PathParam("id") id: Long): Map<String, Any> {
return mapOf(
"id" to id,
"name" to "用户$id",
"email" to "user$id@example.com"
)
}
@Route("/", HttpMethod.POST, authRequired = true)
fun createUser(@Body userData: Map<String, Any>): Map<String, Any> {
return mapOf(
"success" to true,
"message" to "用户创建成功",
"data" to userData,
"id" to System.currentTimeMillis()
)
}
@Route("/{id}", HttpMethod.PUT, authRequired = true)
fun updateUser(
@PathParam("id") id: Long,
@Body userData: Map<String, Any>
): Map<String, Any> {
return mapOf(
"success" to true,
"message" to "用户更新成功",
"id" to id,
"data" to userData
)
}
@Route("/{id}", HttpMethod.DELETE, authRequired = true)
fun deleteUser(@PathParam("id") id: Long): Map<String, Any> {
return mapOf(
"success" to true,
"message" to "用户删除成功",
"id" to id
)
}
}
@Router("/api/products", "产品管理")
class ProductController {
@Route("/search", HttpMethod.GET)
fun searchProducts(
@QueryParam("keyword") keyword: String?,
@QueryParam("category") category: String?
): Map<String, Any> {
return mapOf(
"keyword" to keyword,
"category" to category,
"products" to listOf(
mapOf("id" to 1, "name" to "产品1", "price" to 99.99),
mapOf("id" to 2, "name" to "产品2", "price" to 199.99)
)
)
}
}
// ================ 使用示例 ================
fun main() {
// 注册控制器
val userController = UserController()
val productController = ProductController()
RouterTable.register(userController)
RouterTable.register(productController)
// 打印所有路由
println("已注册的路由:")
RouterTable.getAllRoutes().forEach { route ->
println(" ${route.method} ${route.path} (需要认证: ${route.authRequired})")
}
// 创建路由器
val routerHandler = RouterHandler()
// 模拟请求
println("\n模拟请求:")
// 请求1: 获取用户列表(不需要认证)
val request1 = RequestContext(
path = "/api/users/",
method = HttpMethod.GET,
headers = emptyMap(),
pathParams = emptyMap(),
queryParams = mapOf(
"page" to listOf("1"),
"size" to listOf("10")
),
body = null
)
val response1 = routerHandler.handleRequest(request1)
println("请求1结果: ${response1.statusCode} - ${response1.body}")
// 请求2: 获取用户详情(需要认证但未提供)
val request2 = RequestContext(
path = "/api/users/123",
method = HttpMethod.GET,
headers = emptyMap(),
pathParams = mapOf("id" to "123"),
queryParams = emptyMap(),
body = null
)
val response2 = routerHandler.handleRequest(request2)
println("请求2结果: ${response2.statusCode} - ${response2.body}")
// 请求3: 获取用户详情(带认证)
val request3 = RequestContext(
path = "/api/users/123",
method = HttpMethod.GET,
headers = mapOf("Authorization" to "Bearer token123"),
pathParams = mapOf("id" to "123"),
queryParams = emptyMap(),
body = null
)
val response3 = routerHandler.handleRequest(request3)
println("请求3结果: ${response3.statusCode} - ${response3.body}")
// 请求4: 创建用户
val request4 = RequestContext(
path = "/api/users/",
method = HttpMethod.POST,
headers = mapOf("Authorization" to "Bearer token123"),
pathParams = emptyMap(),
queryParams = emptyMap(),
body = "name=王五&email=wangwu@example.com&age=30"
)
val response4 = routerHandler.handleRequest(request4)
println("请求4结果: ${response4.statusCode} - ${response4.body}")
// 请求5: 搜索产品
val request5 = RequestContext(
path = "/api/products/search",
method = HttpMethod.GET,
headers = emptyMap(),
pathParams = emptyMap(),
queryParams = mapOf(
"keyword" to listOf("手机"),
"category" to listOf("电子产品")
),
body = null
)
val response5 = routerHandler.handleRequest(request5)
println("请求5结果: ${response5.statusCode} - ${response5.body}")
}
注解最佳实践总结
1. 选择合适的保留策略
RUNTIME: 运行时处理(反射)CLASS: 编译时处理(字节码操作)SOURCE: 源码级别处理(注解处理器)
2. 注解处理器性能优化
// 使用增量处理
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@IncrementalAnnotationProcessor(IncrementalAnnotationProcessorType.ISOLATING)
class MyProcessor : AbstractProcessor()
// 缓存处理结果
object AnnotationCache {
private val cache = mutableMapOf<String, Any>()
@Synchronized
fun getOrCompute(key: String, compute: () -> Any): Any {
return cache.getOrPut(key, compute)
}
}
3. 元注解使用
// 定义元注解
@Target(AnnotationTarget.ANNOTATION_CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class ExperimentalApi
// 使用元注解
@ExperimentalApi
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class PreviewFeature
4. 编译时常量
const val VERSION = "1.0.0"
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class ApiVersion(val version: String = VERSION)
5. 重复注解
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@Repeatable
annotation class Permission(val value: String)
@Permission("READ")
@Permission("WRITE")
@Permission("EXECUTE")
fun sensitiveOperation() {
// 需要多个权限
}
6. 注解别名
typealias ApiEndpoint = Route
@ApiEndpoint("/api/test", HttpMethod.GET)
fun testEndpoint() {
// 使用别名
}
这些案例展示了Kotlin注解的强大功能,从简单的数据验证到复杂的框架开发,注解都能提供优雅的解决方案。在实际项目中,合理使用注解可以大大提高代码的可维护性和可扩展性。