Silentdoer

导航

Java编译时注解自动生成代码

原文:http://blog.csdn.net/robertcpp/article/details/51628656

经过测试即便生成文件也该是运行时才能生成。

在开始之前,我们首先申明一个非常重要的问题:我们并不讨论那些在运行时(Runtime)通过反射机制运行处理的注解,而是讨论在编译时(Compile time)处理的注解。注解处理器是一个在javac中的,用来编译时扫描和处理的注解的工具。可以为特定的注解,注册自己的注解处理器。

一个注解的注解处理器,以Java代码(或者编译过的字节码)作为输入,生成文件(通常是.java文件)作为输出。可以生成Java代码,这些生成的Java代码是在生成的.java文件中,所以不能修改已经存在的Java类,例如向已有的类中添加方法。这些生成的Java文件,会同其他普通的手动编写的Java源代码一样被javac编译。

 

虚处理器AbstractProcessor

 

我们首先看一下处理器的API。每一个处理器都是继承于AbstractProcessor,如下所示:

  1. public class MyProcessor extends AbstractProcessor {  
  2.   
  3.     @Override  
  4.     public synchronized void init(ProcessingEnvironment env){ }  
  5.   
  6.     @Override  
  7.     public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) { }  
  8.   
  9.     @Override  
  10.     public Set<String> getSupportedAnnotationTypes() { }  
  11.   
  12.     @Override  
  13.     public SourceVersion getSupportedSourceVersion() { }  
  14.   
  15. }  
  • init(ProcessingEnvironment env): 每一个注解处理器类都必须有一个空的构造函数。然而,这里有一个特殊的init()方法,它会被注解处理工具调用,并输入ProcessingEnviroment参数。ProcessingEnviroment提供很多有用的工具类Elements,Types和Filer。
  • process(Set<? extends TypeElement> annotations, RoundEnvironment env): 这相当于每个处理器的主函数main()。 在这里写扫描、评估和处理注解的代码,以及生成Java文件。输入参数RoundEnviroment,可以让查询出包含特定注解的被注解元素。
  • getSupportedAnnotationTypes(): 这里必须指定,这个注解处理器是注册给哪个注解的。注意,它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称。换句话说,在这里定义你的注解处理器注册到哪些注解上。
  • getSupportedSourceVersion(): 用来指定你使用的Java版本。通常这里返回SourceVersion.latestSupported()。然而,如果有足够的理由只支持Java 6的话,也可以返回SourceVersion.RELEASE_6。推荐使用前者。

举一个简单例子

自动生成一个bean的结构文件

  1. 把  
  2. public class Student {  
  3.     public String stu_name;  
  4.     public String stu_id;  
  5.     public int stu_age;  
  6. }  
  7. 转换为  
  8. {class:"com.robert.processor.Student",    
  9.  fields:    
  10.  {    
  11.   stu_name:"java.lang.String",    
  12.   stu_id:"java.lang.String",  
  13.   stu_age:"java.lang.Integer"  
  14.  }    
  15. }   

首先声明注解

  1. package com.robert.processor;  
  2.   
  3. import java.lang.annotation.ElementType;  
  4. import java.lang.annotation.Retention;  
  5. import java.lang.annotation.RetentionPolicy;  
  6. import java.lang.annotation.Target;  
  7.   
  8. @Target({ ElementType.FIELD, ElementType.TYPE })  
  9. @Retention(RetentionPolicy.CLASS)  
  10. public @interface Serialize {  
  11.   
  12. }  

将注解加到Student类上
@Serialize
public class Student 

 

定义自己的解析器

 
  1. package com.robert.processor;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileWriter;  
  5. import java.io.IOException;  
  6. import java.util.ArrayList;  
  7. import java.util.HashMap;  
  8. import java.util.HashSet;  
  9. import java.util.List;  
  10. import java.util.Map;  
  11. import java.util.Set;  
  12.   
  13. import javax.annotation.processing.AbstractProcessor;  
  14. import javax.annotation.processing.ProcessingEnvironment;  
  15. import javax.annotation.processing.RoundEnvironment;  
  16. import javax.lang.model.element.Element;  
  17. import javax.lang.model.element.ElementKind;  
  18. import javax.lang.model.element.TypeElement;  
  19. import javax.lang.model.element.VariableElement;  
  20. import javax.lang.model.util.ElementFilter;  
  21. import javax.lang.model.util.Elements;  
  22.   
  23. public class MyProcessor extends AbstractProcessor {  
  24.   
  25.     // 元素操作的辅助类  
  26.     Elements elementUtils;  
  27.   
  28.     @Override  
  29.     public synchronized void init(ProcessingEnvironment processingEnv) {  
  30.         super.init(processingEnv);  
  31.         elementUtils = processingEnv.getElementUtils();  
  32.     }  
  33.   
  34.     @Override  
  35.     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {  
  36.         // 获得被该注解声明的元素  
  37.         Set<? extends Element> elememts = roundEnv.getElementsAnnotatedWith(Serialize.class);  
  38.         TypeElement classElement = null;// 声明类元素  
  39.         List<VariableElement> fields = null;// 声明一个存放成员变量的列表  
  40.         // 存放二者  
  41.         Map<String, List<VariableElement>> maps = new HashMap<String, List<VariableElement>>();  
  42.         // 遍历  
  43.         for (Element ele : elememts) {  
  44.             // 判断该元素是否为类  
  45.             if (ele.getKind() == ElementKind.CLASS) {  
  46.                 classElement = (TypeElement) ele;  
  47.                 maps.put(classElement.getQualifiedName().toString(), fields = new ArrayList<VariableElement>());  
  48.   
  49.             } else if (ele.getKind() == ElementKind.FIELD) // 判断该元素是否为成员变量  
  50.             {  
  51.                 VariableElement varELe = (VariableElement) ele;  
  52.                 // 获取该元素封装类型  
  53.                 TypeElement enclosingElement = (TypeElement) varELe.getEnclosingElement();  
  54.                 // 拿到key  
  55.                 String key = enclosingElement.getQualifiedName().toString();  
  56.                 fields = maps.get(key);  
  57.                 if (fields == null) {  
  58.                     maps.put(key, fields = new ArrayList<VariableElement>());  
  59.                 }  
  60.                 fields.add(varELe);  
  61.             }  
  62.         }  
  63.   
  64.         for (String key : maps.keySet()) {  
  65.             if (maps.get(key).size() == 0) {  
  66.                 TypeElement typeElement = elementUtils.getTypeElement(key);  
  67.                 List<? extends Element> allMembers = elementUtils.getAllMembers(typeElement);  
  68.                 if (allMembers.size() > 0) {  
  69.                     maps.get(key).addAll(ElementFilter.fieldsIn(allMembers));  
  70.                 }  
  71.             }  
  72.         }  
  73.         generateFile(maps);  
  74.         return true;  
  75.     }  
  76.   
  77.     private void generateFile(Map<String, List<VariableElement>> maps) {  
  78.         File dir = new File(MyProcessor.class.getResource("/").getPath());  
  79.         if (!dir.exists())  
  80.             dir.mkdirs();  
  81.         // 遍历map  
  82.         for (String key : maps.keySet()) {  
  83.   
  84.             // 创建文件  
  85.             File file = new File(dir, key.replaceAll("\\.", "_") + ".txt");  
  86.             try {  
  87.                 /** 
  88.                  * 编写文件内容 
  89.                  */  
  90.                 FileWriter fw = new FileWriter(file);  
  91.                 fw.append("{").append("class:").append("\"" + key + "\"").append(",\n ");  
  92.                 fw.append("fields:\n {\n");  
  93.                 List<VariableElement> fields = maps.get(key);  
  94.   
  95.                 for (int i = 0; i < fields.size(); i++) {  
  96.                     VariableElement field = fields.get(i);  
  97.                     fw.append("  ").append(field.getSimpleName()).append(":")  
  98.                             .append("\"" + field.asType().toString() + "\"");  
  99.                     if (i < fields.size() - 1) {  
  100.                         fw.append(",");  
  101.                         fw.append("\n");  
  102.                     }  
  103.                 }  
  104.                 fw.append("\n }\n");  
  105.                 fw.append("}");  
  106.                 fw.flush();  
  107.                 fw.close();  
  108.   
  109.             } catch (IOException e) {  
  110.                 e.printStackTrace();  
  111.             }  
  112.         }  
  113.     }  
  114.   
  115.     @Override  
  116.     public Set<String> getSupportedAnnotationTypes() {  
  117.         Set<String> set = super.getSupportedAnnotationTypes();  
  118.         if (set == null) {  
  119.             set = new HashSet<>();  
  120.         }  
  121.         set.add("com.robert.processor.Serialize");  
  122.         return set;  
  123.     }  
  124. }  

 

我们经常使用的ButterKnife这个框架就很好的使用了AbstractProcessor

 

Butter Knife 是 Android 视图字段和方法绑定,使用注解处理来生成样板代码。后面做详细说明。

posted on 2017-07-14 17:21  Silentdoer  阅读(1276)  评论(0编辑  收藏  举报