[转]java注解与APT技术
下面是一个简单的自定义注解的栗子:
package annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
- 动物名称注解
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AnimalName {
String value() default "";
}
定义了注解,并在需要的时候给相关类,类属性加上注解信息,如果没有响应的注解信息处理流程,注解可以说是没有实用价值,接下来要介绍的是 java 的 APT(Annotation Process Tool) 技术,用于处理自定义注解。
2,APT 技术
APT(Annotation Process Tool),是一种在代码编译时处理注解,按照一定的规则,生成相应的 java 文件,多用于对自定义注解的处理,目前比较流行的 Dagger2, ButterKnife, EventBus3 都是采用 APT 技术,对运行时的性能影响很小。我们通过自定义注解的方式,来了解一下如何使用 APT:
1,自定义注解:
@Target({ElementType.TYPE}) ---作用范围 Class
@Retention(RetentionPolicy.CLASS) --- 生命周期:仅保留到.class 文件
public @interface Route {
/** Path of route*/
String value(); ---类似于成员变量
}
2,使用方式
@Route(path = "/test/activity2")
public class MainActivity extends AppCompatActivity {
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">onCreate</span><span class="hljs-params">(Bundle savedInstanceState)</span> {
<span class="hljs-built_in">super</span>.onCreate(savedInstanceState);
setContentView(R.layout.activity_test2);
}
}
JVM 默认只会处理 @override 等语言自带的注解,对于自定义的注解,需要我们自己处理,java 提供了一个名为 AbstractProcessor.java 的抽象类,我们只要继承该类,就实现自己的注解处理器,来处理自定义的 @Route 注解
public class RouteProcessor extends AbstractProcessor {
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">synchronized</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">init</span><span class="hljs-params">(ProcessingEnvironment env)</span>{
<span class="hljs-comment">// 主要做一些初始化操作</span>
}
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">process</span><span class="hljs-params">(Set<? extends TypeElement> annoations, RoundEnvironment env)</span> {
<span class="hljs-comment">//具体处理注解的逻辑,控制代码的生成</span>
processAnnotations();
}
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> Set<String> <span class="hljs-title function_">getSupportedAnnotationTypes</span><span class="hljs-params">()</span> {
<span class="hljs-comment">// 支持处理的注解类型, 在这里就是@Route</span>
}
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> SourceVersion <span class="hljs-title function_">getSupportedSourceVersion</span><span class="hljs-params">()</span> {
<span class="hljs-comment">//java版本 如:jdk1.6or jdk1.7</span>
}
}
3,自定义 Processor
@AutoService(Processor.class)
public class HelloProcessor extends AbstractProcessor {
<span class="hljs-comment">/** 文件相关的辅助类 用于生成新的源文件、class等 */</span>
<span class="hljs-keyword">private</span> Filer mFiler;
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">synchronized</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">init</span><span class="hljs-params">(ProcessingEnvironment processingEnv)</span> {
<span class="hljs-built_in">super</span>.init(processingEnv);
mFiler = processingEnv.getFiler();
}
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">process</span><span class="hljs-params">(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)</span> {
<span class="hljs-comment">// 构建方法 此处使用到了square公司的javapoet库,用来辅助生成 类的代码</span>
MethodSpec.<span class="hljs-type">Builder</span> <span class="hljs-variable">methodBuilder</span> <span class="hljs-operator">=</span> MethodSpec.methodBuilder(<span class="hljs-string">"show"</span>)
.addModifiers(Modifier.PUBLIC);
methodBuilder.addStatement(<span class="hljs-string">"String test = \"$N\" "</span>,<span class="hljs-string">"hello annotation world!"</span>);
<span class="hljs-comment">/** 构建类 */</span>
<span class="hljs-type">TypeSpec</span> <span class="hljs-variable">finderClass</span> <span class="hljs-operator">=</span> TypeSpec.classBuilder(<span class="hljs-string">"Hello$$Inject"</span>)
.addModifiers(Modifier.PUBLIC)
.addMethod(methodBuilder.build())
.build();
<span class="hljs-keyword">try</span> {
JavaFile.builder(<span class="hljs-string">"com.win.test"</span>,finderClass).build().writeTo(mFiler);
} <span class="hljs-keyword">catch</span> (IOException e) {
e.printStackTrace();
}
<span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
}
<span class="hljs-comment">// 支持的注解类型</span>
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> Set<String> <span class="hljs-title function_">getSupportedAnnotationTypes</span><span class="hljs-params">()</span> {
Set<String> types = <span class="hljs-keyword">new</span> <span class="hljs-title class_">LinkedHashSet</span><>();
types.add(Hello.class.getCanonicalName());
<span class="hljs-keyword">return</span> types;
}
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">public</span> SourceVersion <span class="hljs-title function_">getSupportedSourceVersion</span><span class="hljs-params">()</span> {
<span class="hljs-keyword">return</span> <span class="hljs-built_in">super</span>.getSupportedSourceVersion();
}
}
在 AS 工程中使用
@Hello("MainTest") // 自定义的 Hello 注解
public class MainActivity extends AppCompatActivity {
<span class="hljs-meta">@Override</span>
<span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">onCreate</span><span class="hljs-params">(Bundle savedInstanceState)</span> {
<span class="hljs-built_in">super</span>.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
在项目 App module 的 build 目录下,便会生成对应的 java 类文件:
这个只是简单的栗子,我们可以在 process() 方法中,加入更多的业务逻辑,以实现特定功能。