Jeta:如何创建自定义注释处理器

Jeta: How to create custom annotation processors

plenty of features 已在 Jeta 上可用,但如果缺少某些内容怎么办。我可以创建自己的注释并为其生成元代码吗?

需要有关如何创建自定义 Jeta 处理器的分步教程。

如何创建自定义处理器,分步教程

第 1 步:Hello, World 项目

对于本教程,让我们创建一个简单的 Gradle 项目,其中包含一个模块 app 和一个 class SayHelloApp。 class 将 Hello, World! 写入标准输出。

为了说明,我们将创建 Hello 注释,将 Hello, Jeta! 字符串设置为注释字段。

第 2 步:common 模块

首先,我们需要一个可以在 appapt(即将创建)模块中访问的模块。在 common 模块中,我们需要两个 classes - Hello 注释和 HelloMetacode 接口:

第 3 步:apt 模块

apt - 是一个模块,我们将在其中创建代码生成所需的所有 classes。对于本教程,我们需要一个处理器来处理我们的 Hello 注释。

请注意,此模块依赖于 common 模块,因此我们使用 Hello 注释作为超级构造函数的参数。通过这样做,我们对 Jeta 说我们需要用给定类型注释的所有元素。该模块还依赖于 jeta-apt 以访问 Jeta classes.

第 4 步:处理器

已创建 SayHelloProcessor 现在什么都不做。让我们在其中添加一些逻辑。这里的想法是生成 java 代码,将 Hello, Jeta 字符串设置为用 Hello.

注释的字段

请注意 Jeta 使用 JavaPoet 创建 java 源代码。 Square 的框架真的很棒。请在 GitHub.

上查看

首先,我们需要 metacode 实现 HelloMetacode。为此,我们将向 builder:

添加超级接口
MetacodeContext context = roundContext.metacodeContext();
ClassName masterClassName = ClassName.get(context.masterElement());
builder.addSuperinterface(ParameterizedTypeName.get(
      ClassName.get(HelloMetacode.class), masterClassName));

接下来,通过创建void setHello(M master)方法实现HelloMetacode

MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("setHello")
    .addAnnotation(Override.class)
    .addModifiers(Modifier.PUBLIC)
    .returns(void.class)
    .addParameter(masterClassName, "master");

最后,Hello注释的每个元素的语句,即Jeta通过roundContext参数传入process方法:

for (Element element : roundContext.elements()) {
    String fieldName = element.getSimpleName().toString();
    methodBuilder.addStatement("master.$L = \"Hello, Jeta\"", fieldName);
}

这是完整的 SayHelloProcessor 清单:

package org.brooth.jeta.samples.apt;

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;
import org.brooth.jeta.apt.MetacodeContext;
import org.brooth.jeta.apt.RoundContext;
import org.brooth.jeta.apt.processors.AbstractProcessor;

import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;

public class SayHelloProcessor extends AbstractProcessor {

    public SayHelloProcessor() {
      super(Hello.class);
    }

    @Override
    public boolean process(TypeSpec.Builder builder, RoundContext roundContext) {
      MetacodeContext context = roundContext.metacodeContext();
      ClassName masterClassName = ClassName.get(context.masterElement());
      builder.addSuperinterface(ParameterizedTypeName.get(
            ClassName.get(HelloMetacode.class), masterClassName));

      MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("setHello")
            .addAnnotation(Override.class)
            .addModifiers(Modifier.PUBLIC)
            .returns(void.class)
            .addParameter(masterClassName, "master");

      for (Element element : roundContext.elements()) {
        String fieldName = element.getSimpleName().toString();
        methodBuilder.addStatement("master.$L = \"Hello, Jeta\"", fieldName);
      }

      builder.addMethod(methodBuilder.build());
      return false;
    }
}

第 5 步:元代码

代码生成 classes 所需的所有内容均已创建,我们已准备好尝试。但首先,我们需要添加 jeta.properties 文件以配置 Jeta。您可以找到有关此文件的更多详细信息 on this page。该文件应位于根包中。对于我们的教程,其内容为:

metasitory.package=org.brooth.jeta.samples
processors.add=org.brooth.jeta.samples.apt.SayHelloProcessor

接下来,修改SayHelloApp。我们将在其上放置 Hello 注释,而不是初始化 text 字段:

public class SayHelloApp {
     @Hello
     String text;
}

build.gradle

group 'org.brooth.jeta-samples'
version '1.0'

buildscript {
    repositories {
        maven {
            url 'https://plugins.gradle.org/m2/'
        }
    }
    dependencies {
        classpath 'net.ltgt.gradle:gradle-apt-plugin:0.5'
    }
}

apply plugin: 'net.ltgt.apt'
apply plugin: 'java'

sourceCompatibility = 1.7

repositories {
    mavenCentral()
    jcenter()
}

compileJava {
    options.sourcepath = files('src/main/java')
}

dependencies {
    apt project(':apt')
    compile project(':common')
    compile 'org.brooth.jeta:jeta:+'
}

现在我们可以生成元代码了。 运行 控制台中的下一个命令:

./gradlew assemble

如果到目前为止没有问题,我们将在 app/build 目录下看到 SayHelloApp_Metacode 文件:

第 6 步:控制器

Controllers are the classes that apply metacode to the masters。让我们在 app 模块中为 HelloMetacode 创建一个:

 package org.brooth.jeta.samples;

 import org.brooth.jeta.MasterController;
 import org.brooth.jeta.metasitory.Metasitory;

 public class SayHelloController<M> extends MasterController<M, HelloMetacode<M>> {

     public SayHelloController(Metasitory metasitory, M master) {
       super(metasitory, master, Hello.class, false);
     }

     public void setHello() {
       for (HelloMetacode<M> metacode : metacodes)
         metacode.setHello(master);
     }
 }

第 7 步:MetaHelper

MetaHelper 是一个简单的静态助手 class。如果您对静态助手不满意,则不应在您的项目中使用它。您可以在 this page.

上阅读有关此 class 的更多详细信息

无论如何,让我们在app模块中创建MetaHelper

package org.brooth.jeta.samples;

import org.brooth.jeta.metasitory.MapMetasitory;
import org.brooth.jeta.metasitory.Metasitory;

public class MetaHelper {

    private static MetaHelper instance;
    private final Metasitory metasitory;

    public static MetaHelper getInstance() {
      if (instance == null)
        instance = new MetaHelper("org.brooth.jeta.samples");
      return instance;
    }

    private MetaHelper(String metaPackage) {
      metasitory = new MapMetasitory(metaPackage);
    }

    public static void setHello(Object master) {
      new SayHelloController<>(getInstance().metasitory, master).setHello();
    }
}

请注意,我们必须将我们在 jeta.properties 中指定为 metasitory.package 的相同包 ("org.brooth.jeta.samples") 传递给 MapMetasitory

第 8 步:用法

最后一步 - 我们调用 MetaHelper 的方法。这是 SayHelloApp 的完整列表:

package org.brooth.jeta.samples;

public class SayHelloApp {

    @Hello
    String text;

    public SayHelloApp() {
      MetaHelper.setHello(this);
    }

    public void sayHello() {
      System.out.print(text);
    }

    public static void main(String[] args) {
      new SayHelloApp().sayHello();
    }
}

终于可以运行SayHelloApp了。在控制台中我们应该看到:

Hello, Jeta

链接

快乐的代码生成! :)