一小时搞明白注解处理器Annotation Processor Tool.docx
- 文档编号:23891214
- 上传时间:2023-05-21
- 格式:DOCX
- 页数:19
- 大小:20.57KB
一小时搞明白注解处理器Annotation Processor Tool.docx
《一小时搞明白注解处理器Annotation Processor Tool.docx》由会员分享,可在线阅读,更多相关《一小时搞明白注解处理器Annotation Processor Tool.docx(19页珍藏版)》请在冰豆网上搜索。
一小时搞明白注解处理器AnnotationProcessorTool
一小时搞明白注解处理器(AnnotationProcessorTool)
什么是注解处理器?
注解处理器是(AnnotationProcessor)是javac的一个工具,用来在编译时扫描和编译和处理注解(Annotation)。
你可以自己定义注解和注解处理器去搞一些事情。
一个注解处理器它以Java代码或者(编译过的字节码)作为输入,生成文件(通常是java文件)。
这些生成的java文件不能修改,并且会同其手动编写的java代码一样会被javac编译。
看到这里加上之前理解,应该明白大概的过程了,就是把标记了注解的类,变量等作为输入内容,经过注解处理器处理,生成想要生成的java代码。
处理器AbstractProcessor
处理器的写法有固定的套路,继承AbstractProcessor。
如下:
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
publicclassMyProcessorextendsAbstractProcessor{
@Override
publicsynchronizedvoidinit(ProcessingEnvironmentprocessingEnv){
super.init(processingEnv);
}
@Override
publicSet
returnnull;
}
@Override
publicSourceVersiongetSupportedSourceVersion(){
returnSourceVersion.latestSupported();
}
@Override
publicbooleanprocess(Set
extendsTypeElement>annotations,RoundEnvironmentroundEnv){
returntrue;
}
}
init(ProcessingEnvironmentprocessingEnv)被注解处理工具调用,参数ProcessingEnvironment提供了Element,Filer,Messager等工具
getSupportedAnnotationTypes()指定注解处理器是注册给那一个注解的,它是一个字符串的集合,意味着可以支持多个类型的注解,并且字符串是合法全名。
getSupportedSourceVersion指定Java版本
process(Set
extendsTypeElement>annotations,RoundEnvironmentroundEnv)这个也是最主要的,在这里扫描和处理你的注解并生成Java代码,信息都在参数RoundEnvironment里了,后面会介绍。
在Java7中还可以使用
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
@SupportedSourceVersion(SourceVersion.latestSupported())
@SupportedAnnotationTypes({
//合法注解全名的集合
})
代替getSupportedSourceVersion()和getSupportedAnnotationType(),没毛病,还可以在注解处理离器中使用注解。
注册注解处理器
打包注解处理器的时候需要一个特殊的文件javax.annotation.processing.Processor在META-INF/services路径下
[plain]viewplaincopy在CODE上查看代码片派生到我的代码片
--myprcessor.jar
----com
------example
--------MyProcessor.class
----META-INF
------services
--------javax.annotation.processing.Processor
打包进javax.annotation.processing.Processor的内容是处理器的合法全称,多个处理器之间换行。
[plain]viewplaincopy在CODE上查看代码片派生到我的代码片
com.example.myprocess.MyProcessorA
com.example.myprocess.MyProcessorB
google提供了一个注册处理器的库
[plain]viewplaincopy在CODE上查看代码片派生到我的代码片
compile'com.google.auto.service:
auto-service:
1.0-rc2'
一个注解搞定:
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
@AutoService(Processor.class)
publicclassMyProcessorextendsAbstractProcessor{
...
}
读到这里ButterKnife用到的知识点我们都已经了解了
1.自定义注解
2.用注解处理器解析注解
3.解析完成后生成Java文件
BufferKnife使用:
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
publicclassMainActivityextendsAppCompatActivity{
@Bind(R.id.rxjava_demo)
ButtonmRxJavaDemo;
@Override
protectedvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
mRxJavaDemo.setText("Text");
}
}
然后我们编译一下,打开路径:
/app/build/intermediates/classes/release/com/ming/rxdemo/MainActivity$$ViewBinder.class
这就是我们生成的Java文件,可以看到Button已经在bind里面初始化了。
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
publicclassMainActivity$$ViewBinder
publicMainActivity$$ViewBinder(){
}
publicvoidbind(Finderfinder,Ttarget,Objectsource){
Viewview=(View)finder.findRequiredView(source,2131492944,"field\'mRxJavaDemo\'");
target.mRxJavaDemo=(Button)finder.castView(view,2131492944,"field\'mRxJavaDemo\'");
}
publicvoidunbind(Ttarget){
target.mRxJavaDemo=null;
}
}
接下来我们创建一个项目,写一个简单的用注解绑定控件的例子
项目结构
[plain]viewplaincopy在CODE上查看代码片派生到我的代码片
--apt-demo
----bindview-annotation(JavaLibrary)
----bindview-api(AndroidLibrary)
----bindview-compiler(JavaLibrary)
----app(AndroidApp)
bindview-annotation注解声明
bindview-api调用AndroidSDKAPI
bindview-compiler注解处理器相关
app测试App
1.在bindview-annotation下创建一个@BindView注解,该注解返回一个值,整型,名字为value,用来表示控件ID。
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public@interfaceBindView{
/**
*用来装id
*
*@return
*/
intvalue();
}
2.在bindview-compiler中创建注解处理器BindViewProcessor并注册,做基本的初始化工作。
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
@AutoService(Processor.class)
publicclassBindViewProcessorextendsAbstractProcessor{
/**
*文件相关的辅助类
*/
privateFilermFiler;
/**
*元素相关的辅助类
*/
privateElementsmElementUtils;
/**
*日志相关的辅助类
*/
privateMessagermMessager;
/**
*解析的目标注解集合
*/
privateMap
@Override
publicsynchronizedvoidinit(ProcessingEnvironmentprocessingEnv){
super.init(processingEnv);
mElementUtils=processingEnv.getElementUtils();
mMessager=processingEnv.getMessager();
mFiler=processingEnv.getFiler();
}
@Override
publicSet
Set
types.add(BindView.class.getCanonicalName());//返回该注解处理器支持的注解集合
returntypes;
}
@Override
publicSourceVersiongetSupportedSourceVersion(){
returnSourceVersion.latestSupported();
}
@Override
publicbooleanprocess(Set
extendsTypeElement>annotations,RoundEnvironmentroundEnv){
returntrue;
}
}
是不是注意到了里面有个Map容器,而且类型是AnnotatedClass,这是干啥的呢?
这个很好理解,我们在解析XML,解析Json的时候数据解析完之后是不是要以对象的形式表示出来,这里也一样,@BindView用来标记类成员,一个类下可以有多个成员,好比一个Activity中可以有多个控件,一个容器下有多个控件等。
如下:
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
packagecom.mingwei.myprocess.model;
importcom.mingwei.myprocess.TypeUtil;
importcom.squareup.javapoet.ClassName;
importcom.squareup.javapoet.JavaFile;
importcom.squareup.javapoet.MethodSpec;
importcom.squareup.javapoet.ParameterizedTypeName;
importcom.squareup.javapoet.TypeName;
importcom.squareup.javapoet.TypeSpec;
importjava.util.ArrayList;
importjava.util.List;
importjavax.lang.model.element.Modifier;
importjavax.lang.model.element.TypeElement;
importjavax.lang.model.util.Elements;
/**
*Createdbymingweion12/10/16.
*CSDN:
*Github:
*/
publicclassAnnotatedClass{
/**
*类名
*/
publicTypeElementmClassElement;
/**
*成员变量集合
*/
publicList
/**
*元素辅助类
*/
publicElementsmElementUtils;
publicAnnotatedClass(TypeElementclassElement,ElementselementUtils){
this.mClassElement=classElement;
this.mElementUtils=elementUtils;
this.mFiled=newArrayList<>();
}
/**
*获取当前这个类的全名
*/
publicStringgetFullClassName(){
returnmClassElement.getQualifiedName().toString();
}
/**
*添加一个成员
*/
publicvoidaddField(BindViewFieldfield){
mFiled.add(field);
}
/**
*输出Java
*/
publicJavaFilegenerateFinder(){
returnnull;
}
/**
*包名
*/
publicStringgetPackageName(TypeElementtype){
returnmElementUtils.getPackageOf(type).getQualifiedName().toString();
}
/**
*类名
*/
privatestaticStringgetClassName(TypeElementtype,StringpackageName){
intpackageLen=packageName.length()+1;
returntype.getQualifiedName().toString().substring(packageLen).replace('.','$');
}
}
成员用BindViewField表示,没什么复杂的逻辑,在构造函数判断类型和初始化,简单的get函数
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
packagecom.mingwei.myprocess.model;
importcom.mingwe.myanno.BindView;
importjavax.lang.model.element.Element;
importjavax.lang.model.element.ElementKind;
importjavax.lang.model.element.Name;
importjavax.lang.model.element.VariableElement;
importjavax.lang.model.type.TypeMirror;
/**
*Createdbymingweion12/10/16.
*CSDN:
*Github:
*被BindView注解标记的字段的模型类
*/
publicclassBindViewField{
privateVariableElementmFieldElement;
privateintmResId;
publicBindViewField(Elementelement)throwsIllegalArgumentException{
if(element.getKind()!
=ElementKind.FIELD){//判断是否是类成员
thrownewIllegalArgumentException(String.format("Onlyfieldcanbeannotatedwith@%s",
BindView.class.getSimpleName()));
}
mFieldElement=(VariableElement)element;
//获取注解和值
BindViewbindView=mFieldElement.getAnnotation(BindView.class);
mResId=bindView.value();
if(mResId<0){
thrownewIllegalArgumentException(String.format("value()in%sforfield%isnotvalid",
BindView.class.getSimpleName(),mFieldElement.getSimpleName()));
}
}
publicNamegetFieldName(){
returnmFieldElement.getSimpleName();
}
publicintgetResId(){
returnmResId;
}
publicTypeMirrorgetFieldType(){
returnmFieldElement.asType();
}
}
这里看到了很多的Element,在Xml解析时候就有Element这个概念。
在Java源文件中同样有Element概念:
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
packagecom.example;//PackageElement
publicclassMyClass{//TypeElement
privateinta;//VariableElement
privateFooother;//VariableElement
publicFoo(){}//ExecuteableElement
publicvoidsetA(//ExecuteableElement
intnewA//TypeElement
){
}
}
接下来就是在处理器的process中解析注解了
每次解析前都要清空,因为process方法可能不止走一次。
拿到注解模型之后遍历调用生成Java代码
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
@Override
publicbooleanprocess(Set
extendsTypeElement>annotations,RoundEnvironmentroundEnv){
mAnnotatedClassMap.clear();
try{
processBindView(roundEnv);
}catch(IllegalArgumentExceptione){
error(e.getMessage());
returntrue;
}
try{
for(AnnotatedClassannotatedClass:
mAnnotatedClassMap.values()){
info("generatingfilefor%s",annotatedClass.getFullClassName());
annotatedClass.generateFinder().writeTo(mFiler);
}
}catch(Exceptione){
e.printStackTrace();
error("Generatefilefailed,reason:
%s",e.getMessage());
}
returntrue;
}
processBindView和getAnnotatedClass
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
/**
*
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 一小时搞明白注解处理器Annotation Processor Tool 一小时 明白 注解 处理器 Annotation