apt:

@Retention后面的值,设置的为CLASS,说明就是编译时动态处理的。一般这类注解会在编译的时候,根据注解标识,动态生成一些类或者生成一些xml都可以,在运行时期,这类注解是没有的~~会依靠动态生成的类做一些操作,因为没有反射,效率和直接调用方法没什么区别~~~

RUNTIME, 说明就是运行时动态处理,这个大家见得应该最多,在运行时拿到类的Class对象,然后遍历其方法、变量,判断有无注解声明,然后做一些事情。

SOURCE,标记一些信息,这么说可能太抽象,那么我说,你见过@Override、@SuppressWarnings等,这类注解就是用于标识,可以用作一些检验

@Target表示该注解可以用于什么地方,可能的类型TYPE(类),FIELD(成员变量)

 1 public enum ElementType {
 2     /**
 3      * Class, interface or enum declaration.
 4      */
 5     TYPE,
 6     /**
 7      * Field declaration.
 8      */
 9     FIELD,
10     /**
11      * Method declaration.
12      */
13     METHOD,
14     /**
15      * Parameter declaration.
16      */
17     PARAMETER,
18     /**
19      * Constructor declaration.
20      */
21     CONSTRUCTOR,
22     /**
23      * Local variable declaration.
24      */
25     LOCAL_VARIABLE,
26     /**
27      * Annotation type declaration.
28      */
29     ANNOTATION_TYPE,
30     /**
31      * Package declaration.
32      */
33     PACKAGE
34 } 

TypeElement  :类

JavaPoet源码初探

TypeSpec是类型元素的抽象,通过Kind枚举定义class、interface、enum、annotation四种类型。

MethodSpec代表方法的抽象。

1     // 元素操作的辅助类
2     Elements elementUtils;
3     //文件相关的辅助类
4     private Filer mFiler;
5     //日志相关的辅助类
6     private Messager mMessager;

1、先添加两个java library,一个annotation,一个compiler:

annotation library:用于放置注解。

build.gradle文件:注意属于java library,不使用android library。

1  apply plugin: 'java'
2
3  sourceCompatibility = 1.7
4  targetCompatibility = 1.7
5  dependencies {
6      compile fileTree(dir: 'libs', include: ['*.jar'])
7  }

compile library:用于放置根据注解生成代码类。

build.gradle文件:注意属于java library,不使用android library。

 1 apply plugin: 'java'
 2
 3 sourceCompatibility = 1.7
 4 targetCompatibility = 1.7
 5 dependencies {
 6     compile fileTree(dir: 'libs', include: ['*.jar'])
 7
 8     compile 'com.google.auto.service:auto-service:1.0-rc2'
 9     compile 'com.squareup:javapoet:1.7.0'
10     <!-- 引用annotation library  -->
11     compile project(':annotation')
12 }

app module:android app

build.gradle文件:

 1 apply plugin: 'com.android.application'
 2 apply plugin: 'com.neenbedankt.android-apt'
 3
 4 android {
 5     compileSdkVersion 23
 6     buildToolsVersion "23.0.2"
 7
 8     defaultConfig {
 9         applicationId "com.example.aptdemo"
10         minSdkVersion 15
11         targetSdkVersion 23
12         versionCode 1
13         versionName "1.0"
14     }
15     buildTypes {
16         release {
17             minifyEnabled false
18             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19         }
20     }
21 }
22
23 dependencies {
24     compile fileTree(dir: 'libs', include: ['*.jar'])
25     testCompile 'junit:junit:4.12'
26     compile 'com.android.support:appcompat-v7:24.0.0'
27
28     compile project(':annotation')
29     apt project(':compiler')
30 }

官方有一个列子的:生成一个java类,然后java 类里面有个main函数:

// 注解类 (目录:annotation library)

package com.example;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Test {
}

// apt生成类 (目录:compiler library)

 1 package com.example;
 2
 3 import com.google.auto.service.AutoService;
 4 import com.squareup.javapoet.JavaFile;
 5 import com.squareup.javapoet.MethodSpec;
 6 import com.squareup.javapoet.TypeSpec;
 7 import java.io.IOException;
 8 import java.util.Collections;
 9 import java.util.Set;
10 import javax.annotation.processing.AbstractProcessor;
11 import javax.annotation.processing.Processor;
12 import javax.annotation.processing.RoundEnvironment;
13 import javax.lang.model.SourceVersion;
14 import javax.lang.model.element.Modifier;
15 import javax.lang.model.element.TypeElement;
16
17 @AutoService(Processor.class)
18 public class TestProcessor extends AbstractProcessor {
19
20     /***
21      package com.example.helloworld;
22
23      public final class HelloWorld {
24         public static void main(String[] args) {
25             System.out.println("Hello, JavaPoet!");
26         }
27      }
28      */
29
30     @Override
31     public Set<String> getSupportedAnnotationTypes() {
32         return Collections.singleton(Test.class.getCanonicalName());
33     }
34
35     @Override
36     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
37
38         MethodSpec main = MethodSpec.methodBuilder("main")
39                 .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
40                 .returns(void.class)
41                 .addParameter(String[].class, "args")
42                 .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
43                 .build();
44
45         TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
46                 .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
47                 .addMethod(main)
48                 .build();
49
50         JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
51                 .build();
52
53         try {
54             javaFile.writeTo(processingEnv.getFiler());
55         } catch (IOException e) {
56             e.printStackTrace();
57         }
58
59         return false;
60     }
61
62     @Override
63     public SourceVersion getSupportedSourceVersion() {
64         return SourceVersion.RELEASE_7;
65     }
66 }

官方例子

// 使用:

在随意一个类上面加上@Test,编译时,代码就会生成。

下面就是简单的view注解生成代码:

// 注解类 (目录:annotation library)

1、activity 注解 :作用于类,所以目标类型是类。

1 @Target(ElementType.TYPE)
2 @Retention(RetentionPolicy.CLASS)
3 public @interface DIActivity {
4 }

2、view 注解:作用于字段,所以目标类型是字段。

1 @Target(ElementType.FIELD)
2 @Retention(RetentionPolicy.RUNTIME)
3 public @interface DIView {
4
5     int value() default 0;
6 }

3、onClick 注解:作用于方法,所以目标类型是方法。

1 @Target(ElementType.METHOD)
2 @Retention(RetentionPolicy.RUNTIME)
3 public @interface DIOnClick {
4
5     int[] value() default 0;
6 }

// 使用工具类:

ViewInject:用于统一activity 使用方法,注入。

 1 public class ViewInject {
 2
 3     public final static void bind(Activity activity){
 4
 5         String className = activity.getClass().getName();
 6         try {
 7             Class<?> aClass = Class.forName(className + "$ViewInject");
 8             Inject inject = (Inject) aClass.newInstance();
 9             inject.bindView(activity);
10         } catch (Exception e){
11             e.printStackTrace();
12         }
13     }
14 }

Inject:用于apt生成类继承,统一父类。

1 public interface Inject<T> {
2
3     void bindView(T host);
4 }

// apt生成类 (目录:compiler library)

DIProcessor:

  1 @AutoService(Processor.class)
  2 public class DIProcessor extends AbstractProcessor {
  3
  4     // 元素操作的辅助类
  5     Elements elementUtils;
  6     //文件相关的辅助类
  7     private Filer mFiler;
  8     //日志相关的辅助类
  9     private Messager mMessager;
 10
 11     @Override
 12     public synchronized void init(ProcessingEnvironment processingEnv) {
 13         super.init(processingEnv);
 14         // 元素操作的辅助类
 15         elementUtils = processingEnv.getElementUtils();
 16         mFiler = processingEnv.getFiler();
 17         mMessager = processingEnv.getMessager();
 18     }
 19
 20     /**
 21      * 指定哪些注解应该被注解处理器注册
 22      * @return
 23      */
 24     @Override
 25     public Set<String> getSupportedAnnotationTypes() {
 26         /**
 27          * Set<String> types = new LinkedHashSet<>();
 28          types.add(BindView.class.getCanonicalName());
 29          types.add(OnClick.class.getCanonicalName());
 30          return types;
 31          * */
 32         // 规定需要处理的注解
 33         return Collections.singleton(DIActivity.class.getCanonicalName());
 34     }
 35
 36     @Override
 37     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
 38
 39         Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(DIActivity.class);
 40
 41         for(Element element : elements){
 42
 43             // 判断是否Class
 44             TypeElement typeElement = (TypeElement) element;
 45
 46             List<? extends Element> members = elementUtils.getAllMembers(typeElement);
 47
 48             MethodSpec.Builder bindViewMethod = MethodSpec.methodBuilder("bindView")
 49                     .addModifiers(Modifier.PUBLIC)
 50                     .returns(TypeName.VOID)
 51                     .addParameter(ClassName.get(typeElement.asType()), "activity", Modifier.FINAL);
 52
 53             bindViewMethod.addStatement("mContext = activity");
 54
 55             MethodSpec.Builder onClickMethod = MethodSpec.methodBuilder("onClick")
 56                     .addAnnotation(Override.class)
 57                     .addModifiers(Modifier.PUBLIC)
 58                     .returns(TypeName.VOID)
 59                     .addParameter(ClassName.get("android.view", "View"), "view");
 60
 61 //            TypeSpec.Builder listener = TypeSpec.anonymousClassBuilder("")
 62 //                    .addSuperinterface(ClassName.get("android.view", "View", "OnClickListener"));
 63
 64             mMessager.printMessage(Diagnostic.Kind.NOTE, "非常厉害");
 65
 66 //            bindViewMethod.addStatement("View.OnClickListener listener = null");
 67             onClickMethod.addCode("switch(view.getId()) {");
 68
 69             for (Element item : members) {
 70                 // handler the findViewById
 71                 DIView diView = item.getAnnotation(DIView.class);
 72                 if (diView != null){
 73
 74                     bindViewMethod.addStatement(String.format("activity.%s = (%s) activity.findViewById(%s)"
 75                             ,item.getSimpleName(), ClassName.get(item.asType()).toString(), diView.value()));
 76                     continue;
 77                 }
 78
 79                 // handler the setOnViewClick
 80                 DIOnClick diOnClick = item.getAnnotation(DIOnClick.class);
 81                 if(diOnClick != null) {
 82
 83                     if(diOnClick.value().length > 1) {
 84                         for (int index = 0; index < diOnClick.value().length; index++) {
 85                             onClickMethod.addCode("case $L: ", diOnClick.value()[index]);
 86
 87                             bindViewMethod.addStatement("activity.findViewById($L).setOnClickListener(this)", diOnClick.value()[index]);
 88                         }
 89
 90                         onClickMethod.addStatement("mContext.$N()", item.getSimpleName());
 91
 92                         onClickMethod.addStatement("break");
 93                     }
 94
 95                     if(diOnClick.value().length == 1) {
 96                         onClickMethod.addCode("case $L: ", diOnClick.value()[0]);
 97                         onClickMethod.addStatement("mContext.$N()", item.getSimpleName());
 98                         onClickMethod.addStatement("break");
 99
100                         bindViewMethod.addStatement("activity.findViewById($L).setOnClickListener(this)", diOnClick.value()[0]);
101                     }
102                     continue;
103                 }
104             }
105
106             onClickMethod.addStatement("}");
107
108 //            TypeSpec onClickListener = listener.addMethod(onClickMethod.build()).build();
109 //            bindViewMethod.addStatement("listener = $L ", onClickListener);
110
111             List<MethodSpec> methodSpecs = new ArrayList<>();
112             methodSpecs.add(bindViewMethod.build());
113             methodSpecs.add(onClickMethod.build());
114
115             List<TypeName> typeNames = new ArrayList<>();
116             typeNames.add(ClassName.get("android.view", "View", "OnClickListener"));
117             typeNames.add(ParameterizedTypeName.get(ClassName.get("com.example.charles.aptdemo.inject", "Inject"), TypeName.get(typeElement.asType())));
118
119             FieldSpec fieldSpec = FieldSpec.builder(TypeName.get(typeElement.asType()), "mContext").build();
120
121             TypeSpec typeSpec = TypeSpec.classBuilder(element.getSimpleName() + "$ViewInject")
122 //                    .superclass(TypeName.get(typeElement.asType()))
123                     .addSuperinterfaces(typeNames)
124                     .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
125                     .addMethods(methodSpecs)
126                     .addField(fieldSpec)
127                     .build();
128
129             JavaFile javaFile = JavaFile.builder(getPackageName(typeElement), typeSpec).build();
130
131             try {
132                 javaFile.writeTo(processingEnv.getFiler());
133             } catch (IOException e) {
134                 e.printStackTrace();
135             }
136
137             mMessager.printMessage(Diagnostic.Kind.NOTE, "完成");
138         }
139
140         return true;
141     }
142
143     private String getPackageName(TypeElement type) {
144         return elementUtils.getPackageOf(type).getQualifiedName().toString();
145     }
146
147     /**
148      * 指定使用的 Java 版本。通常返回SourceVersion.latestSupported()。
149      * @return
150      */
151     @Override
152     public SourceVersion getSupportedSourceVersion() {
153         return SourceVersion.RELEASE_7;
154     }
155 }

DIProcessor

// 使用方式:

 1 @DIActivity
 2 public class MainActivity extends AppCompatActivity {
 3
 4     @DIView(R.id.text)
 5     public TextView textView;
 6
 7     @Override
 8     protected void onCreate(Bundle savedInstanceState) {
 9         super.onCreate(savedInstanceState);
10         setContentView(R.layout.activity_main);
11
12         ViewInject.bind(this);
13
14         textView.setText("我不是这样子的");
15     }
16
17     @DIOnClick({R.id.btn_change_text, R.id.btn_change})
18     public void changeTextView(){
19         textView.setText("我被点击了");
20     }
21
22     @DIOnClick(R.id.btn_change_new)
23     public void changeNew(){
24         textView.setText("new");
25     }
26 }

关于javapoet:

JavaPoet 是一个用来生成 .java源文件的Java API。

下列文章为apt

Android注解-编译时生成代码 (APT)

下列文章为反射:

Android 打造编译时注解解析框架 这只是一个开始

Android 进阶 教你打造 Android 中的 IOC 框架 【ViewInject】 (上)

Android 进阶 教你打造 Android 中的 IOC 框架 【ViewInject】 (下)

转载于:https://www.cnblogs.com/CharlesGrant/p/5811338.html

apt 根据注解,编译时生成代码相关推荐

  1. Android中使用AbstractProcessor在编译时生成代码

    Android中使用AbstractProcessor在编译时生成代码 发现这边不错的文章,忍不住转了过来,转自: http://blog.csdn.net/industriously/article ...

  2. Flutter编译时生成代码之 code_builder

    前言 之前学习原生开发的时候使用过各种编译自动生成模板代码的框架,例如ARouter,这些框架其实是借助了JavaPoet 这个框架来自动生成代码的,JavaPoet 可以在编译自动生成模板代码,在f ...

  3. 云炬Android开发笔记 8代码生成器设计与实践(仿ButterKnife注解框架,编译期生成代码)

    阅读目录 1.编写自己的元注解和annotationProcessor 1.1 微信登录说明 1.2 butterKnifer的元注解 1.3 模仿的butterKnifer的元注解 2. 通过注解生 ...

  4. 如何用 APT(Annotation Processing Tool)自动生成代码

    我们很多人都写过代码自动生成的工具,比如用python结合moko模板引擎,或者java 结合freemarker模板引擎,protoc 等,实现解析策划数据类,proto协议类,或者数据库层的实体类 ...

  5. linux内核编译的image,内核编译时生成uImage的办法

    有一个很简单的办法u-boot编译结束时,会在tool文件夹下面生成一个mkimage文件,将这个文件复制到交叉编译器目录下的bin文件夹下面,以后编译时就会生成uImage文件,省的用命令行的方式转 ...

  6. 【错误记录】Android Studio 编译时 Kotlin 代码编译报错 ( 升级支持库时处理 @NonNull 参数 )

    文章目录 一.报错信息 二.报错分析 三.解决方案 一.报错信息 最近处理支持库 , 将所有的支持库都升级到了 28.0.028.0.028.0.0 ; implementation 'com.and ...

  7. 匿名内部类编译时生成多个class文件

    由于线上代码出问题,需要增量上线.就遇到了以下问题:个性一个内部类后重新挂载对应的class文件,结果发现无效. 增量上线就是把你更改的java文件在本地编译成为.class文件,然后直接将.clas ...

  8. 【错误记录】Android Studio 编译时 Kotlin 代码编译报错 ( Not enough information to infer type variable T )

    文章目录 一.报错信息 二.解决方案 一.报错信息 在 Kotlin 代码中调用 findViewById(R.id.button) 代码 , 编译时报如下错误信息 : Not enough info ...

  9. 在 C# 中生成代码的四种方式——包括.NET 5中的Source Generators

    Microsoft在最新的C#版本中引入了Source Generator.这是一项新功能,可以让我们在代码编译时生成源代码.在本文中,我将介绍四种C#中的代码生成方式,以简化我们的日常工作.然后,您 ...

最新文章

  1. Python基础--Python3基础语法
  2. 数据访问层代码自动生成
  3. 【Arthas】Arthas 导出堆栈信息
  4. 设计一个扩展自抽象类geometricobject的新的triangle类_面向对象设计原则之开放封闭原则(开闭原则OCP)...
  5. 语言题库安装包312mb_大学为什么要考取计算机二级,以后很需要,附二级Msoffice题库...
  6. windows及iis网站配置https证书
  7. c语言函数制作,C语言库函数制作方法
  8. 全球与中国引文管理软件市场深度研究分析报告
  9. 1.2 批量生成MySQL建表语句
  10. matlab+nnf.m,第6章_西安电子科技大学:工程线性代数(MATLAB版)_ppt_大学课件预览_高等教育资讯网...
  11. oracle11g dataguard安装实施
  12. PMP培训费和考试费
  13. annaconda 安装 opencv(cv2)
  14. 帝国时代之罗马复兴玩法技巧
  15. 水生生物学类毕业论文文献包含哪些?
  16. 哪些手机系统必备,却鲜为人知的APP?
  17. LTE下行物理层传输机制-PCFICH信道
  18. Frustratingly Simple Few-Shot Object Detection
  19. 揭示十年数据库经验,告诉你如何轻松应对常见问题(SQL 小虚竹)
  20. 软考中级 信息系统管理工程师考试真题(2019上半年下午)

热门文章

  1. RocketMQ消息重试机制
  2. Spring Boot 集成 WebSocket通信信息推送!
  3. 项目管理基础:系统分析相关概念介绍
  4. 一篇故事讲述了计算机网络里的基本概念:网关,DHCP,IP寻址,ARP欺骗,路由,DDOS等...
  5. ASP.NET 4.0 取消表单危险字符验证
  6. deepin桌面为什么那么卡_deepin因NVIDIA显卡造成开机启动问题:卡在开机logo界面+进入桌面鼠标一直转圈...
  7. html为什么要进行表单验证_化学锚栓为什么要进行拉拔试验?
  8. STM32F1 GPIO工作原理初探
  9. windows中安装zookeeper
  10. 您的UX库不只是书籍