????????之前我们可能用过dagger、hilt之类的注解,使用这些注解可以方便我们的工作,减少我们的代码编写量。因此,本文主要是介绍如何自定义一个注解处理器。可以分为2个部分,一、定义注解和注解处理器;二、注解使用演示。
????????本文的目标:定义一个类构造方法上的注解DemoAnnotation,并使用此注解生成一个工厂类。注解的执行结果如下图所示
一、注解和注解处理器
? ? ? ? 首先我们要新建一个”Java or Kotlin Library“组件项目 ,然后 对此子项目的build.gradle文件进行配置,如下:
plugins {
id 'java-library'
id 'kotlin'
id 'kotlin-kapt'
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.5.31"
kapt 'com.google.auto.service:auto-service:1.0-rc6'
compileOnly 'com.google.auto.service:auto-service:1.0-rc6'
}
????????1、定义注解
????????我们定义一个在类构造方法上的注解,如下所示:
package com.example.demoprocessor
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CONSTRUCTOR)
annotation class DemoAnnotation
? ? ? ? 2、定义注解处理器
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes({DemoProcessor.DEMO_ANNOTATION})
public class DemoProcessor extends AbstractProcessor {
static final String DEMO_ANNOTATION = "com.example.demoprocessor.DemoAnnotation";
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return new DemoAnnotationProcessor().process(annotations, roundEnv, processingEnv);
}
}
? ? ? ? 我们需要使用@SupportedAnnotationTypes指明注解的名称(包名+注解名),然后使用@AutoService注解,作用是可以帮我们在注解路径里自动添加当前的注解类。
? ? ? ? 本文注解处理器的实现放在DemoAnnotationProcessor类中,如下所示:
package com.example.demoprocessor
import java.io.IOException
import javax.annotation.processing.ProcessingEnvironment
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.element.ExecutableElement
import javax.lang.model.element.Modifier
import javax.lang.model.element.PackageElement
import javax.lang.model.element.TypeElement
import javax.tools.JavaFileObject
class DemoAnnotationProcessor {
fun process(
annotations: Set<TypeElement>,
roundEnv: RoundEnvironment,
processingEnv: ProcessingEnvironment
): Boolean {
println("start process")
if (roundEnv.rootElements.size == 0) {
return false
}
for (annotation in annotations) {
// 留下需要的注解
if (!annotation.qualifiedName.toString().contains(DEMO_ANNOTATION)) {
continue
}
val elements = roundEnv.getElementsAnnotatedWith(annotation)
for (element in elements) {
// 去掉非方法的注解
if (element !is ExecutableElement) {
continue
}
// 检查被注解的方法是否符合要求
if (!checkHasNoErrors(element)) {
continue
}
// 获取类
val classElement = element.enclosingElement as TypeElement
// 获取包
val packageElement = getPackageElement(classElement)
createServiceClass(
packageElement!!.qualifiedName.toString(),
classElement.simpleName.toString(),
classElement.simpleName.toString(),
processingEnv
)
}
}
return true
}
private fun createServiceClass(
pkName: String,
simpleClazzName: String,
serviceName: String,
processingEnv: ProcessingEnvironment
) {
println("createServiceClass")
val className = "$pkName.$simpleClazzName"
val builder = StringBuilder()
.append("package com.example.annotationprocessor.generated;\n\n")
.append("import $className;\n\n")
.append("public class GeneratedClass$serviceName {\n\n") // open class
.append("\tpublic $simpleClazzName getInstance() {\n") // open method
.append("\t\treturn ")
builder.append("new $simpleClazzName()")
builder.append(";\n") // end return
.append("\t}\n") // close method
.append("}\n") // close class
try { // write the file
val source: JavaFileObject = processingEnv.filer
.createSourceFile(
"com.example.annotationprocessor.generated.GeneratedClass"
+ serviceName
)
val writer = source.openWriter()
writer.write(builder.toString())
writer.flush()
writer.close()
} catch (e: IOException) {
// Note: calling e.printStackTrace() will print IO errors
// that occur from the file already existing after its first run, this is normal
}
}
private fun getPackageElement(subscriberClass: TypeElement): PackageElement? {
var candidate = subscriberClass.enclosingElement
while (candidate !is PackageElement) {
candidate = candidate.enclosingElement
}
return candidate
}
private fun checkHasNoErrors(initMethod: ExecutableElement): Boolean {
if (!initMethod.modifiers.contains(Modifier.PUBLIC)) {
return false
}
return initMethod.parameters.size <= 0
}
private final val DEMO_ANNOTATION = "com.example.demoprocessor.DemoAnnotation"
}
? ? ? ? 注意在此过程中,只能创建文件,而不能修改已经存在的文件。
二、注解的使用
? ? ? ? 首先,我们需要在app项目的build.gradle里添加对注解处理的依赖。注意,要使用kapt或annotationProcessor添加对注解处理器的依赖,然后使用implement 添加对注解的依赖。因为本文将注解和注解处理器都放在了一个组件里,所以会用kapt和implement对同一组件添加依赖,如下所示:
implementation project(':demoprocessor')
kapt project(":demoprocessor")
? ? ? ? 在添加完成之后,就可以使用注解了,如下:
class TestAnnotation @DemoAnnotation constructor(){
init {
// 模拟初始化操作
var i = 0
i++
}
}
? ? ? ? 最后,在build之后,生成的注解文件如开篇的第一张图所示。
|