本文一定可能有一定的错误(误导性内容),希望dalao指正!

注解

定义在类、方法、字段 、参数前的”注释“。注解(annotation)可以被编译器打包进class文件。

元注解

有一些注解可以修饰其他注解,这些注解就称为元注解(meta annotation)。

常用 元注解有:

  • @Retention:指定该注解是否存储在编译后的 class 文件中,以及它在运行时能否通过反射可见 (默认都是 true);
  • @Target:指定可以用该注解标注的元素的可能的类型(类、函数、属性、表达式等);
  • @Repeatable:允许在单个元素上多次使用相同的该注解;
  • @MustBeDocumented:指定该注解是公有 API 的一部分,并且应该包含在生成的 API 文档中显示的类或方法的签名中。

运行时注解

运行时注解@Retention(AnnotationRetention.RUNTIME),大多数时候是在运行时使用反射来实现所需效果。

Java可以使用反射API读取Annotation的方法有:

  • X.isAnnotationPresent(Class):判断某个注解是否存在于X
  • X.getAnnotation(Class):读取Annotation

编译时注解

注解处理器(annotation processor)是javac的一个工具,它用来在编译时扫描和处理注解。你可以自定义注解,并注册相应的注解处理器,用于处理你的注解逻辑。

在项目结构中创建Java or Kotlin Library类型的模块,在其中实现一个自定义注解处理器(AbstractProcessor),并重写process方法。随后你就可以在process方法内对注解进行处理。

注册注解处理器

AbstractProcessor在实现之后不能直接使用,需要注册之后才能运行,有两种方式:

  1. 手动注册

在实现AbstractProcessor的项目中添加resources/META-INF文件夹,并在META-INF下创建一个名为javax.annotation.processing.Processor的文本文件,在里面写入实现AbstractProcessor类的全名。

  1. 自动注册

使用google提供的AutoService主动注册。在AbstractProcessor实现类上加上注解:

1
2
3
4
5
6
7
@AutoService(Processor::class)
class EventBusProcessor : AbstractProcessor() {}

// 导入依赖库
kapt("com.google.auto.service:auto-service:1.0.1") // kotlin

// 推荐手动注册,自动注册可能由于Bug不能正常注册。

启用注解处理器

build.gradle.kts(:app)的依赖配置中添加以下内容来启用你所自定义注解处理器

1
2
\\ :AbstractProcessor为你自己的注解处理器模块
kapt(project(":AbstractProcessor"))

使用注解处理器处理注解

你可以使用KotlinPoet来生成你的代码,使用前先添加依赖:

1
implementation("com.squareup:kotlinpoet:1.14.2")

关于KotlinPoet的详细教程你可以在官方文档中查阅。
这是一个使用案例(其实是从自己的破烂项目拔下来的):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package top.artmoe.abstractprocessor

import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.buildCodeBlock
import javax.annotation.processing.AbstractProcessor
import javax.annotation.processing.RoundEnvironment
import javax.annotation.processing.SupportedAnnotationTypes
import javax.annotation.processing.SupportedSourceVersion
import javax.lang.model.SourceVersion
import javax.lang.model.element.TypeElement

@SupportedSourceVersion(SourceVersion.RELEASE_17)
@SupportedAnnotationTypes("top.artmoe.inao.hook.base.annotation.HookItem")
class HookItemProcessor : AbstractProcessor() {

private val allItemClassNames = mutableListOf<String>()

override fun process(p0: MutableSet<out TypeElement>, p1: RoundEnvironment): Boolean {
println("[Start] build Annotation processor")
// 获取所有使用HookItem注解的类
for (element in p0){
for (anno in p1.getElementsAnnotatedWith(element)) {
allItemClassNames.add("${anno.enclosingElement}.${anno.simpleName}")
}
}
val allHookItemClass = PropertySpec.builder("allHookItemClass",List::class.parameterizedBy(Any::class))
.initializer("%L",buildCodeBlock {
val sb = StringBuilder()
for (itemClassName in allItemClassNames){
sb.append("${itemClassName}::class.java").append(",")
}
add("listOf<Class<*>>(${sb})")
})
.build()
val allHookItem = TypeSpec.objectBuilder("AllHookItem")
.addProperty(allHookItemClass)
.build()

val file = FileSpec.builder("top.artmoe.inao.hook", "AllHookItem")
.addType(allHookItem)
.build()
try {
file.writeTo(processingEnv.filer)
} catch (_: Exception) {
}
return true
}
}

上面代码的生成文件为AllHookItem.kt,内容为:

1
2
3
4
5
6
7
8
9
package top.artmoe.inao.hook

import kotlin.Any
import kotlin.collections.List

public object AllHookItem {
public val allHookItemClass: List<Any> =
listOf<Class<*>>(top.artmoe.inao.hook.item.NtQQSettingInject::class.java,top.artmoe.inao.hook.item.NtQQSettingPurify::class.java)
}

至此,你对注解可能有一定了解了,但可能还不够,来日方长。