Java 进阶之 Java 注解二

title date tags Java注解二 2

title date tags

Java注解二

2016-08-30 06:52:43 -0700

Java进阶

摘要:

​ 本篇将会介绍使用apt工具实现注解处理器。

[TOC]

使用apt工具实现Java注解处理器

使用之前,请确认JAVA的环境变量已经配置成功,并且把tools.jar加入到自己电脑的环境变量中,在这里我使用的是IntelliJ IDEA (开发注解以及解析库,并且打出jar包)和Android studio(测试自己打出的jar包的使用情况),使用apt实现的注解处理器是编译时处理注解。

使用apt处理注解关键是继承 AbstractProcessor ,实现 process 方法。

下面就让我们一步一步的实现一个简单的例子:

  1. 首先,创建一个Maven支持的Java标准工程,新建过程按照下图所示顺序:

    Java 进阶之 Java 注解二

    Java 进阶之 Java 注解二

    Java 进阶之 Java 注解二

    新建好工程之后,我又创建了一个包,下图是目前的项目目录:

    Java 进阶之 Java 注解二

  2. 定义一个名为AptAnnotation的注解,其中如果@Target 和@Retention不了解的可以去Java注解一 进行了解

    package iuni.life;

    import java.lang.annotation.ElementType;

    import java.lang.annotation.Retention;

    import java.lang.annotation.RetentionPolicy;

    import java.lang.annotation.Target;

    /**

    * Created by iuni.life on 16/8/30.

    * yangfei's computer

    */

    @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})

    @Retention(RetentionPolicy.SOURCE)

    public @interface AptAnnotation {

    String author() default "iuni.life";

    String date();

    int version() default 1;

    }

  3. 继承AbstractProcessor,实现process

    • 新建一个AptAnnotationProcessor.class并继承自AbstractProcessor,可能会出现"Usage of API documented as @since 1.6+" 这个错误,出现这个错误就要修改语言等级,语言等级修改成1.6以上就可以了,我们修改成1.7(因为是在Android studio测试,而Android studio中支持Java8的jack编译器并不支持apt插件),如下是修改流程:

      Java 进阶之 Java 注解二

      Java 进阶之 Java 注解二

      点击Apply,OK 即可。

    • 至此,我们可以放心的写我们的代码了,下面是注解处理代码:

      package iuni.life;

      import javax.annotation.processing.*;

      import javax.lang.model.SourceVersion;

      import javax.lang.model.element.Element;

      import javax.lang.model.element.PackageElement;

      import javax.lang.model.element.TypeElement;

      import javax.tools.Diagnostic;

      import javax.tools.JavaFileObject;

      import java.io.IOException;

      import java.io.PrintWriter;

      import java.io.Writer;

      import java.util.Set;

      /**

      * Created by iuni.life on 16/8/30.

      * yangfei's computer

      */

      //支持的注解类型,此处要填写全名

      @SupportedAnnotationTypes("iuni.life.AptAnnotation")

      ////支持的JDk版本,我用的是1.7

      @SupportedSourceVersion(SourceVersion.RELEASE_7)

      public class AptAnnotationProcessor extends AbstractProcessor {

      //类名的前缀、后缀

      public static final String SUFFIX = "AutoGenerate";

      public static final String PREFIX = "My_";

      public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

      for (TypeElement typeElement : annotations) {

      for (Element e : roundEnv.getElementsAnnotatedWith(typeElement)) {

      //准备在gradle的控制台打印信息

      Messager messager = processingEnv.getMessager();

      //打印

      messager.printMessage(Diagnostic.Kind.NOTE, "Printing:" + e.toString());

      messager.printMessage(Diagnostic.Kind.NOTE, "Printing:" + e.getSimpleName());

      messager.printMessage(Diagnostic.Kind.NOTE, "Printing:" + e.getEnclosedElements().toString());

      //获取注解

      AptAnnotation annotation = e.getAnnotation(AptAnnotation.class);

      //获取元素名并将其首字母大写

      String name = e.getSimpleName().toString();

      char c = Character.toUpperCase(name.charAt(0));

      name = String.valueOf(c + name.substring(1));

      //包裹注解元素的元素, 也就是其父元素, 比如注解了成员变量或者成员函数, 其上层就是该类

      Element enclosingElement = e.getEnclosingElement();

      //获取父元素的全类名,用来生成报名

      String enclosingQualifiedname;

      if (enclosingElement instanceof PackageElement) {

      enclosingQualifiedname = ((PackageElement) enclosingElement).getQualifiedName().toString();

      } else {

      enclosingQualifiedname = ((TypeElement) enclosingElement).getQualifiedName().toString();

      }

      try {

      //生成包名

      String generatePackageName = enclosingQualifiedname.substring(0, enclosingQualifiedname.lastIndexOf("."));

      // 生成的类名

      String genarateClassName = PREFIX + enclosingElement.getSimpleName() + SUFFIX;

      //创建Java 文件

      JavaFileObject f = processingEnv.getFiler().createSourceFile(genarateClassName);

      // 在控制台输出文件路径

      messager.printMessage(Diagnostic.Kind.NOTE, "Printing: " + f.toUri());

      Writer w = f.openWriter();

      try {

      PrintWriter pw = new PrintWriter(w);

      pw.println("package " + generatePackageName + ";");

      pw.println("/npublic class " + genarateClassName + " { ");

      pw.println("/n /** 打印值 */");

      pw.println(" public static void print" + name + "() {");

      pw.println(" // 注解的父元素: " + enclosingElement.toString());

      pw.println(" System.out.println(/"代码生成的路径: " + f.toUri() + "/");");

      pw.println(" System.out.println(/"注解的元素: " + e.toString() + "/");");

      pw.println(" System.out.println(/"注解的版本: " + annotation.version() + "/");");

      pw.println(" System.out.println(/"注解的作者: " + annotation.author() + "/");");

      pw.println(" System.out.println(/"注解的日期: " + annotation.date() + "/");");

      pw.println(" }");

      pw.println("}");

      pw.flush();

      } finally {

      w.close();

      }

      } catch (IOException e1) {

      processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,

      e1.toString());

      }

      }

      }

      //该方法返回ture表示该注解已经被处理, 后续不会再有其他处理器处理; 返回false表示仍可被其他处理器处理.

      return true;

      }

      }

    • 说明:

      该段代码其实就是解析注解并获取需要的值,然后使用JavaFileObject生成相关的Java代码。

  4. 现在我们需要生成Jar文件,需要修改pom.xml,默认生成的pom.xml需要再添加jar,和maven-compiler-plugin,修改后如下所示:

    <?xml version="1.0" encoding="UTF-8"?>

    <project xmlns="http://maven.apache.org/POM/4.0.0"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>iuni.life</groupId>

    <artifactId>iuni</artifactId>

    <version>1.0-SNAPSHOT</version>

    <packaging>jar</packaging>

    <build>

    <plugins>

    <plugin>

    <artifactId>maven-compiler-plugin</artifactId>

    <version>2.3.2</version>

    <configuration>

    <source>1.6</source>

    <target>1.6</target>

    <!-- Disable annotation processing for ourselves. -->

    <compilerArgument>-proc:none</compilerArgument>

    </configuration>

    </plugin>

    </plugins>

    </build>

    </project>

  5. 打成jar文件步骤如下图流程所示: Java 进阶之 Java 注解二

    Java 进阶之 Java 注解二

    Java 进阶之 Java 注解二

Java 进阶之 Java 注解二

目前为止,项目目录应该是下图所示: Java 进阶之 Java 注解二

  1. 在resources资源文件夹下新建META-INF/services/javax.annotation.processing.Processor文件,在META-INF中显示标识,以便AptAnnotationProcessor被使用,(其中services需要自己新建Directory) Java 进阶之 Java 注解二

    目前为止,项目目录应该是这样: Java 进阶之 Java 注解二 在javax.annotation.processing.Processor中写入我们的AptAnnotationProcessor完整名称:“iuni.life.AptAnnotationProcessor”,如下图: Java 进阶之 Java 注解二

  2. 执行“Make Project”

    • 执行过程中如出现“ Error:java: Compilation failed: internal java compiler error ”这个错误,则需要去修改项目的JDK版本,详见下图:

      Java 进阶之 Java 注解二

    • 若出现”javax.annotation.processing.Processor: Provider me.generator.GenerateInterfaceProcessor not found“这个异常,则需要将javax.annotation.processing.Processor文件中的内容删掉,然后make两次,然后在加入原来的内容,然后在make。

  3. 生成jar文件

    在执行步骤7无错误的情况下会在如下的文件目录下生成jar包:![javaApt15](Java注解二/javaApt15.png)

使用Android Studio验证生成的jar包

  1. 使用android Studio新建一个测试项目,把生成的JavaAptAnnotation.jar拷贝到测试项目的libs,并add as library至该测试项目。

  2. 为使用注解,需要对测试项目的gradle 进行如下配置:

    • 项目根目录gradle中build.gradle的dependencies添加:“classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' ”

    • 执行完上述步骤之后的build.gradle如下图所示:

      Java 进阶之 Java 注解二

  3. 同样也要对Moudle的build.gradle进行配置

    • 首先引入Android-apt 插件,即在Moudle的build.gradle加入:

      • apply plugin: 'android-apt'
    • 执行完上述步骤之后的Moudle的build.gradle如下图所示:

      Java 进阶之 Java 注解二

  4. 编写测试,我们只做一个简单的测试,在MainActivity中使用我们自己定义的注解,如下:

    package com.yf.androidannotation;

    import android.support.v7.app.AppCompatActivity;

    import android.os.Bundle;

    import iuni.life.AptAnnotation;

    public class MainActivity extends AppCompatActivity {

    @Override

    protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    test();

    }

    @AptAnnotation(author = "iuni",date = "2016-08-31",version = 1)

    public void test(){}

    }

  5. make Project当前项目,会在如下图找到生成的java文件:

    Java 进阶之 Java 注解二

  6. 这时我们就可以使用生成的文件了

    package com.yf.androidannotation;

    import android.support.v7.app.AppCompatActivity;

    import android.os.Bundle;

    import com.yf.androidannotation.My_MainActivityAutoGenerate;

    import iuni.life.AptAnnotation;

    public class MainActivity extends AppCompatActivity {

    @Override

    protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    test();

    }

    @AptAnnotation(author = "iuni",date = "2016-08-31",version = 1)

    public void test(){

    My_MainActivityAutoGenerate.printTest();

    }

    }

  7. 运行测试,在LogCat中会出现如下图所示:

    Java 进阶之 Java 注解二

有关Java注解的调试详见Java 注解三

后记

文章有不足之后,请大家多多指教,共同学习,共同进步。

未登录用户
全部评论0
到底啦