相信不少开发人员在开发过程中都用到过不少的注解,它能提高我们的工作效率,也让项目和代码结构变得更简洁和清晰。目前已经有很多主流的框架也用到了注解技术。例如:ButterKnife、Dagger2、Retrofit、Glide等。可见,注解变得越来越流行了。
注解一般分为两种:运行时注解、编译时注解。
一般配合反射机制使用,相对编译时注解性能比较低,但是灵活性好。例如:Retrofit用的就是运行时注解。
编译时注解能够自动处理Java源文件,并可以根据需要生成新的文件。
今天我们就来学习一下怎么自定义一个注解处理器。关于注解的基础知识在这里我就不讲了,大家自己去学习哦。
注解处理器一般通过继承AbstractProcessor类来实现,通过process方法进行处理。需要注意的是,注解处理器只能生成新的文件,不能修改已存在的源文件。
首先我们新建一个Java Library项目,来作为注解处理器模块。注意是Java Library,不是Android Library。因为我们要用到的是javax包中的类,而Android Library中的JDK不包含这些类。
项目创建完成后,我们就可以新建自定义注解了,这里我简单的定义了一个注解。该注解就是我们项目中需要用到的注解。
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Template {
}
开始新建我们的自定义注解处理器。这里我新建了一个MyTemplateProcessor类,继承AbstractProcessor。
public class MyTemplateProcessor extends AbstractProcessor {
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
return false;
}
}
它必须要实现一个process方法,这个方法就相当于main函数,我们就是在这个方法中处理注解与生成新文件的。
另外还有一些其他的方法可配置,下面实现了几个常用的方法。其中@SupportedAnnotationTypes和@SupportedSourceVersion这两个注解可以代替getSupportedAnnotationTypes()和getSupportedSourceVersion()方法。
@SupportedAnnotationTypes("com.xuhj.java.processor.Template")
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class MyTemplateProcessor extends AbstractProcessor {
/**
* 该处理器支持的所有注解类集合,在这里可以添加自定义注解
*
* 可以用注解@SupportedAnnotationTypes("com.xuhj.java.processor.Template")
*/
@Override
public Set getSupportedAnnotationTypes() {
Set set = new HashSet<>();
// 添加自定义注解
set.add(Template.class.getCanonicalName());
return set;
}
/**
* 该处理器支持的JDK版本,例如:SourceVersion.RELEASE_7
* 一般返回SourceVersion.latestSupported()
*
* 可以用注解@SupportedSourceVersion(SourceVersion.RELEASE_7)
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
/**
* 该初始化方法会被注解处理工具调用,并传入参数processingEnvironment,
* 该参数提供了很多有用的工具类,例如Elements、Types、Filter等等
*/
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
}
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
return false;
}
}
接下来我们来写process方法中的逻辑,这里就是自动生成代码文件的地方。
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
StringBuilder sb = new StringBuilder("package com.xuhj.java.processor;\n")
.append("public class GeneratedTemplate{\n")
.append("\tpublic String getMessage(){\n")
.append("\t\treturn \"");
for (Element element : roundEnvironment.getElementsAnnotatedWith(Template.class)) {
String objectType = element.getSimpleName().toString();
sb.append(objectType).append(" say hello!\\n");
}
sb.append("\";\n")
.append("\t}\n")
.append("}\n");
try {
JavaFileObject source = processingEnv.getFiler()
.createSourceFile("com.xuhj.java.processor.generated.GeneratedTemplate");
Writer writer = source.openWriter();
writer.write(sb.toString());
writer.flush();
writer.close();
} catch (Exception e) {
}
return true;
}
到这里,一个自定义注解处理器已经算是写完了,接下来就可以准备集成到项目中了。
在使用之前,我们还需要声明注解处理器。这里我们在main目录下,新建/resources/META-INF.services目录,并新建一个文件javax.annotation.processing.Processor
在该文件中用文本声明我们的注解处理器
com.xuhj.java.processor.MyTemplateProcessor
然后执行Make Project,就可以在build目录下生成jar包了。我们可以直接拿jar包集成到项目中使用了。
上面这种声明方式有点麻烦,想我这种懒人能偷懒就偷懒。这里用到了Google开源的AutoService库。在build.gradle中加入依赖
dependencies {
implementation ‘com.google.auto.service:auto-service:1.0-rc4’
}
然后在自定义注解处理器类中添加注解@AutoService(Processor.class)
@AutoService(Processor.class)
public class MyTemplateProcessor extends AbstractProcessor {
}
然后执行Make Project就可以和方法一一样,生成jar包。
直接把生成的jar包放到项目的libs中,就可以使用了。执行Make Project,就会在build下生成我们自定义的文件。
GeneratedTemplate类就是我们代码动态生成的文件,内容如下:
package com.xuhj.java.processor;
public class GeneratedTemplate{
public String getMessage(){
return "MyAppGlideModule say hello!\nSampleActivity say hello!\n";
}
}
然后就可以直接在代码中引用该类的方法了,是不是很神奇。O(∩_∩)O
这里我用的是Android studio 3.0,所以在集成到项目中的时候,编译不通过。
Annotation processors must be explicitly declared now. The following dependencies on the compile classpath are found to contain annotation processor. Please add them to the annotationProcessor configuration.
- permissionsdispatcher-processor-2.3.1.jar
Alternatively, set android.defaultConfig.javaCompileOptions.annotationProcessorOptions.includeCompileClasspath = true to continue with previous behavior. Note that this option is deprecated and will be removed in the future.
See https://developer.android.com/r/tools/annotation-processor-error-message.html for more details.
解决方案:
在项目的build.gradle中添加以下
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
includeCompileClasspath true
}
}
}
}
好了,关于自定义注解处理器先讲解到这里。用注解处理器的好处就是可以自动帮我们生成一些重复大量的代码,并且能让我们的类变得干净、逻辑清晰。