必赢亚洲手机app下载


3306抓鸡原理和工具教程766net必赢亚洲手机版

初入坑心得766net必赢亚洲手机版

766net必赢亚洲手机版自定义表明处理器

标签: java annotation


上一篇博客钻探了有关阐明的基础知识,以及运行时(Runtime)通过反射机制来拍卖声明,但既然是Runtime,那么总会有效能上的耗费,如若我们可以在编译期(Compile
time)就能处理注明,这本来更好,而不少框架其实都是在编译期处理声明,比如知名的bufferknife,这一个进程并不复杂,只需要大家自定义声明处理器(Annotation
Processor)就足以了。(Annotation
Processor下文有些地方直接简称处理器,不要领悟成cpu那一个总计机)。

在Compile
time阐明就能起效果,这才是真的反映阐明价值的地点,然则自定义Compile
time的注释处理器也没怎么秘密的。阐明处理器是编译器(javac)的一个工具,它用来在编译时扫描和处理注明。我们可以自定义一个诠释,并编制和挂号对应的电脑。在写法上它其实就是我们自定义一个类,该类
extends javax.annotation.processing.AbstractProcessor
AbstractProcessor是一个abstract的基类。它以咱们写好的java源码或者编译好的代码做为输入,然后就可以透过总括机代码来实现大家所希望的出口了,比如输出一份新的java代码,此时注明管理器就以递归的款式展开多趟处理,直到把代码(包括你手写的代码,以及讲明处理器生成的代码)中颇具的笺注都被处理完毕。

咱俩已经写好的代码即使是无法改改了,不过这并不影响通过声明处理器来生成新的代码。还以bufferknife为例,写findViewById实在太无聊了,所以大家就动用了bufferknife的注释格局简单这多少个历程。

public class TestMainActivity extends BaseActivity {
    @BindView(R.id.mainSwitchGoneBtn)
    Button goneBtn;
    .......
}

不过事实上呢,是bufferknife通过其注明处理器器来生成了对应的代码,它生成的文本是如此的:

public class TestMainActivity_ViewBinding<T extends TestMainActivity> implements Unbinder {
  protected T target;

  @UiThread
  public TestMainActivity_ViewBinding(T target, View source) {
    this.target = target;

    target.goneBtn = Utils.findRequiredViewAsType(source, R.id.mainSwitchGoneBtn, "field 'goneBtn'", Button.class);
  }
}

据此bufferknife就是通过这种措施来麻烦了协调,方便了咱们。

诠释处理器是运行在它和谐的虚拟机jvm当中的,也就是说,javac启动了一个完整的java虚拟机来运作注明处理器,那一点分外首要,因为这注解你编写的阐明处理器代码,和您写的任何java代码是没什么区其余。不管是你利用的API,依然设计时的沉思,编码习惯,甚至你想行使的另外第三方类库,框架等,都是一样的。

认识处理器

面前就说过,我们自定义的进程,就是extends AbstractProcessor,先来看望这么些抽象处理器类。

package com.yaoxiaowen.testprocessor;

import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;

public class TestProcessor extends AbstractProcessor{

    /**
     * 每个注解处理器都必须有一个空的构造方法(父类已经实现了),这个init方法会被构造器调用,
     * 并传入一个 ProcessingEnvironment 参数,该参数提供了很多工具类,
     * 比如 Elements, Filer, Messager, Types
     * @author www.yaoxiaowen.com
     */
    @Override
    public synchronized void init(ProcessingEnvironment env) {
        // TODO Auto-generated method stub
        super.init(env);
    }

    /**
     * 这个方法在父类中是abstract的,所以子类必须实现。
     * 这个方法就是相当于 注解处理器的 入口 main()方法,我们说在编译时,对注解进行的处理,
     * 比如对注解的扫描,评估和处理,以及后续的我们要做的其他操作。(比如生成其他java代码文件),
     * 都是在这里发生的。
     * 
     * 参数RoundEnvironment可以让我们查出包含特定注解的被注解元素。
     * @author www.yaoxiaowen.com
     */
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        // TODO Auto-generated method stub
        return false;
    }

    /**
     * 这个方法虽然在父类当中不是 abstract的,但是我们也必须实现。
     * 因为该方法的作用是指定我们要处理哪些注解的,
     * 比如你想处理注解MyAnnotation,可是该处理器怎么知道你想处理MyAnnotation,而不是OtherAnnotation呢。
     * 所以你要在这里指明,你需要处理的注解的全称。
     * 
     * 返回值是一个字符串的集合,包含着本处理器想要处理的注解类型的合法全称。
     * @author www.yaoxiaowen.com
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        // TODO Auto-generated method stub
        return super.getSupportedAnnotationTypes();
    }

    /**
     * 本方法用来指明你支持的java版本,
     * 不过一般使用 SourceVersion.latestSupported() 就可以了。 
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        // TODO Auto-generated method stub
        return super.getSupportedSourceVersion();
    }
}

这些根本方法,在代码片段的注释已经写的很明白了。

我们选择TestProcessor.java这多少个处理器的目标就是分析处理java代码,而代码是依据一定的布局正式的,代码文件被读取后,各类字符串会被演说成token举办处理,而javac的编译器首先将java代码分解为架空语法树(AST)。而以此布局,在总结机内部,其实是被代表成这么的:

package com.example;    // PackageElement

public class Foo {        // TypeElement

    private int a;      // VariableElement
    private Foo other;  // VariableElement

    public Foo () {}    // ExecuteableElement

    public void setA (  // ExecuteableElement
        int newA   // TypeElement
    ){}
}

总括机在处理代码时,其实就是对抽象语法树举办遍历操作,分解出每一个的类,方法,属性等,然后再将这一个要素的始末展开拍卖。

而实质上,这么些PackageElement,VariableElement等要素模型都是在一个专程的类包中javax.lang.modeljavax.lang.model用来为
Java 编程语言建立模型的包的类和层次结构。
此包及其子包的分子适用于言语建模、语言处理任务和
API(包括但并不仅限于注释处理框架)。

继续 AbstractProcessor实现自定义处理器

俺们现在透过连续AbstractProcessor来实现一个小demo。
流程和意义如下:我们定义了一个声明SQLString,然后实现声明处理器
DbProcessor。该注明处理器效用很粗略,就是生成一个文本,将贯彻了SQLString的性质元素的相关内容写入到这一个文件(比如所在类的名字,属性名,所设置的注脚的值)。

咱俩先自定义一个诠释

package com.yaoxiaowen.comp.proce.db;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.lang.model.element.Element;

/**
 * 该注解的 使用范围是 属性(域) 上
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface SQLString {
    int value() default 0;
    String name() default "";
}

接下来再来定义声明处理器

package com.yaoxiaowen.comp.proce.db;

import java.io.File;
import java.io.FileWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.tools.Diagnostic;

/**
  * @author www.yaoxiaowen.com
  */
public class DbProcessor extends AbstractProcessor{
    private Messager messager;

    private int count = 0;
    private int forCount = 0;
    private StringBuilder generateStr = new StringBuilder();

    @Override
    public synchronized void init(ProcessingEnvironment env) {
        // TODO Auto-generated method stub
        super.init(env);
        messager = env.getMessager();
        String logStr = "enter init(),  进入 init()";
        printMsg(logStr);
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
        // TODO Auto-generated method stub
        String logStr = "enter process(), 进入process";


        //用来 存储 (className, 输出语句) 这种结构 
        Map<String, String> maps = new HashMap<>();

        //得到 使用了 SQLString注解的元素
        Set<? extends Element> eleStrSet = env.getElementsAnnotatedWith(SQLString.class);

        count++;

        for (Element eleStr : eleStrSet){

            //因为我们知道SQLString元素的使用范围是在域上,所以这里我们进行了强制类型转换
            VariableElement eleStrVari = (VariableElement)eleStr;
            forCount++;

            // 得到该元素的封装类型,也就是 包裹它的父类型
            TypeElement enclosingEle = (TypeElement)eleStrVari.getEnclosingElement();
            String className = enclosingEle.getQualifiedName().toString();

            generateStr.append("className = " + className);
            generateStr.append("\t fieldName = " + eleStrVari.getSimpleName().toString());

            //得到在元素上,使用了注解的相关情况
            SQLString sqlString = eleStrVari.getAnnotation(SQLString.class);
            generateStr.append("\t annotationName = " + sqlString.name());
            generateStr.append("\t annotationValue = " + sqlString.value());
            generateStr.append("\t forCount=" + forCount);
            generateStr.append("\n");
        }

        generateStr.append("test File yaowen");
        generateStr.append("\t count=" + count);
        generateFile(generateStr.toString());
        return true;
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        // TODO Auto-generated method stub
        Set<String> strings = new TreeSet<>();
        strings.add("com.yaoxiaowen.comp.proce.db.SQLString");
        return strings;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        // TODO Auto-generated method stub
        return SourceVersion.latestSupported();
    }

    //将内容输出到文件
    private void generateFile(String str){
        try {
            //这是mac环境下的路径
            File file = new File( "/Users/yw/code/dbCustomProcFile");
            FileWriter fw = new FileWriter(file);
            fw.append(str);

            fw.flush();
            fw.close();

        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            printMsg(e.toString());
        }
    }

    private void printMsg(String msg){
        messager.printMessage(Diagnostic.Kind.ERROR, msg);
    }   
}

整合着注释,我们精晓,这么些处理器的职能就是将部分音讯输出到
/Users/yw/code/dbCustomProcFile 这些文件中。

自身在代码中采取了
javax.annotation.processing.Messager来输出一些log音讯,因为这一个进程是在编译时输出的,所以System.out.println()就没用了,这多少个输出音讯是给接纳了该处理器的第三方程序员看的,不是给该总结机的撰稿人看的。
例如demo当中的log代码,在终极成功的打包成jar,在另一个门类中运用时(Android
Studio环境下,Eclipse我愣是没找到哪个窗口输出编译信息),编译时期输出音讯如下:

.......
:app:compileSc_360DebugJavaWithJavac
注: enter init, 进入init
注: enter process, 进入process
注: Creating DefaultRealmModule
注: enter process, 进入process
注: enter process, 进入process
注: 某些输入文件使用了未经检查或不安全的操作。
注: 有关详细信息, 请使用 -Xlint:unchecked 重新编译。

:app:generateJsonModelSc_360Debug UP-TO-DATE
:app:externalNativeBuildSc_360Debug
......

累加注册信息

电脑的代码即使写完了,不过这还没完呢,剩下还有卓殊关键的手续,那就是添加注册信息。因为注脚处理器是属于javac的一个平台级的效率,所以我们的应用方法是将代码打包成jar的款式,那样就足以在任何第三方项目当中使用了。而在打包jar往日,则要在类型中添加注册信息。

先看一下以此目录的结构:
766net必赢亚洲手机版 1

(eclipse)注册的步子如下:
1,选中工程,鼠标右键,New -> Source Folder,创造resources文件夹,然后逐一通过New -> Folder 创设六个文本夹 :
META-INF,services
2,在services文件夹下,New -> File,
创立一个文本,javax.annotation.processing.Processor。在文书中,输入自定义的统计机的人名:
com.yaoxiaowen.comp.proce.db.DbProcessor
766net必赢亚洲手机版,输入之后记得键入回车一下。

事实上这一个手动注册的过程,也是可以不用大家忙碌的。google开发了一个申明工具AutoService,大家得以从来在总计机代码上采纳。类似下边这样:

/**
  * @author www.yaoxiaowen.com
  */
@AutoService(Processor.class)
public class DbProcessor extends AbstractProcessor{
        .......

以此声明工具自动生成META-INF/services/javax.annotation.processing.Processor文件,文件里还含有了计算机的人名:
com.yaoxiaowen.comp.proce.db.DbProcessor

探望此间,你可能会相比震惊,我们在诠释处理器的代码中也得以采用讲明。
这就是说此时请再看看本文开端的这句话

诠释处理器是运作在它自己的虚拟机jvm当中的,也就是说,javac启动了一个完完全全的java虚拟机来运作表明处理器…..

做完这一个,大家的品类就早已做到了,下面要做的就是包裹成jar了。

卷入和使用jar(eclipse为例)

1: 打包jar
眼前说过,编译期的笺注处理器是平台级的成效,是要注册给javac的,
所以需要打包成jar, 我们的连串打包的名字是
AnnoCustomProce.jar

至于现实的打包过程,参见gif图(这是从鸿洋大神的博客上读书到的)。
766net必赢亚洲手机版 2

2: 建立新类型
eclipse下的java项目,新建立一个lib文件夹,然后将AnnoCustomProce.jar手动拷贝到那一个目录下。

3: 引用包,并启用annotation processor。
具体操作见gif图。
766net必赢亚洲手机版 3

要留意一下六个gif图中的各样选项和布局。

利用声明

现好吗,已经大功告成了。上边就是使用了。
新建一个类,使用我们的笺注。

  public class AnnoCreateFile {

    @SQLString(name="yw")
    String filed;
    @SQLString(name="yaow", value=1)
    String name;

    /**
     * @author www.yaoxiaowen.com
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println("hello world");
    }
}

当编译完这一个系列时(eclipse默认就是Build Automatically),我们就能在
/Users/yw/code/目录下找到 dbCustomProcFile
文件了,打开那些文件,内容如下:
className = com.yaoxiaowen.testjar.AnnoCreateFile fieldName = filed annotationName = yw annotationValue = 0 forCount=1 className = com.yaoxiaowen.testjar.AnnoCreateFile fieldName = name annotationName = yaow annotationValue = 1 forCount=2 test File yaowen count=1test File yaowen count=2

功勋卓著告成,我们中标的落实了一个可以在编译时期起功能的自定义注明处理器。

理所当然,这一个demo没有什么样实际效果,它的功效也相当简单,不过了然了这一个进程,我们在骨子里需求当中,就足以由此类似的形式来促成想要的效劳了。

成百上千时候,大家都是梦想讲明处理器是来输出java代码的,既然是代码,那么总有格式的,这就不像简单的文本这样举行输出了,输出java代码,一般采用一个类库:javapoet。而我辈只要在诠释处理器中引入了第三方的类库,那么将其包装成jar的经过,就和大家演示的截然不同。这一点需要活动google。其它,如若想对java源码举行游刃有余的处理,那么需要对此javax.lang.model
包下的各种Elements,工具之类的可比熟识。具体的api,需要参考oracle的文档.

小结和反省

自己在学习自定义阐明处理器的过程中,参考了网上的多多博客,敲代码举行实测,可是事实上仍然碰着了诸多的题目,也折腾了漫长,在此间我将自己所碰着的题材,都位列出来。(即使有些是好笑的低档错误),希望对我们享有协助。

  1. resource/META-INF/services
    com.yaoxiaowen.annotation.createjson.BeanProcessor 文件中,写的是
    处理器的类DbProcessor,而不是你的申明类:SQLString
  2. resource 这些文件夹 是 New Source Folder,前边三个公文,才是 New
    Folder, 关于两者之间的分别:
    后者就是一个惯常的公文夹而已,可是前者,是属于类型的一局部,eclipse会编译这么些文件夹。所以有些著作说建立的
    是res(而不是resource)文件夹,这些实际无所谓,只要它是 Souce Folder.
  3. 在导入包的时候,注意不要导入错误的包(比如import java.awt.Window.Type)。因为屡次同一个类名,它在不同的包里都有落实。像常用的List,通常导错包。java.awt.Listjava.util.ListList相比较常用,我们很容易找到错误,不过
    Type等等的不常用,所以不是那么容易觉察。
  4. 在电脑的更动文书的代码中,有这么一句:

    File file = new File( "/Users/yw/code/dbCustomProcFile");
    

    这假若自己将代码改成这样:

    File file = new File("./dbCustomProcFile");
    

    那么请问这时,这多少个dbCustomProcFile文件到底在这边吗?
    在自身的微处理器上,该公文路径分别如下
    D:\software\eclipse\dbCustomProcFile(window)或/Users/yw//Downloads/Eclipse.app/Contents/MacOS/dbCustomProcFile(mac)。
    诚如大家在工程中采纳./俺们都觉得是工程近来的目录,可是在诠释处理器中,这多少个路子实际上是eclipse
    安装路径下
    javac的门道。的确应该如此,因为注明处理器毕竟是javac的一个工具。

  5. 某五次测试时,当自己向新工程导入生成的jar包时,刚刚导入eclipse就报错。
    766net必赢亚洲手机版 4
    后来好不容易意识了问题所在。
    766net必赢亚洲手机版 5
    在编码过程中,函数的行参原来是processingEnv,后来本身嫌长,就修改为了env,不过下边一句super.init()却遗忘修改了。所以造成找不到这么些参数,而题材是在window和mac下,这句代码IDE都不曾另外指示,我又在下边故意写了一句错代码String = 2,此时IDE指示错误。
    这为啥第一句话IDE就是没报错呢。测试了常常java文件的同连串型的谬误,IDE就会报错,所以这诚然让自己不解。

如上就是本篇小说的全部内容,关于声明处理器深处的不在少数事物其实也没搞懂。也欢迎大家留言指导交流。


作者: www.yaoxiaowen.com

github: https://github.com/yaowen369

欢迎对于自己的博客内容批评指导,如若问题,可评论或邮件(yaowen369@gmail.com)联系

欢迎转载,转载请声明出处.谢谢

相关文章

No Comments, Be The First!
近期评论
    功能
    网站地图xml地图