Android开发Android开发经验谈Android技术知识

JavaPoet + 注解 +注解处理器 优雅的生成代码

2020-04-07  本文已影响0人  大漠孤烟直_v

JavaPoet是square推出的开源java代码生成框架,提供Java Api生成.java源文件。这个框架功能非常有用,我们可以很方便的使用它根据注解、数据库模式、协议格式等来对应生成代码。通过这种自动化生成代码的方式,可以让我们用更加简洁优雅的方式要替代繁琐冗杂的重复工作。

关于JavaPoet 的API使用,官方Github主页已经有很详细的使用说明和示例了,具体可前往查看。此处不赘述,详见 项目主页、源码及使用说明

使用场景

1.根据编译时注解生成代码(例如butterknife)
示例:简单演示利用编译时注解+JavaPoet来实现编译期间动态生成代码:

简单演示利用编译时注解+JavaPoet来实现编译期间动态生成代码:

工程目录结构:

(1)导入依赖
build.gradle (Module:app)

dependencies {
    annotationProcessor 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    implementation project(':annotations')
    annotationProcessor project(':annotation_compiler')
} 

build.gradle (Module:annotation_compiler)

  dependencies {
    ...
    implementation 'com.squareup:javapoet:1.11.1'
    implementation project(':annotations')
    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
    compileOnly('com.google.auto.service:auto-service:1.0-rc4')
}

(2)定义注解(Module:annotations )

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface HelloAnnotation {
}

(3)定义Processor

@AutoService(Processor.class)
public class HelloProcessor extends AbstractProcessor {
    private Filer mFiler;


    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        mFiler = processingEnvironment.getFiler();
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        for (TypeElement element : set) {
            if (element.getQualifiedName().toString().equals(HelloAnnotation.class.getCanonicalName())) {
                MethodSpec spec = MethodSpec.methodBuilder("setName")
                        .addModifiers(Modifier.PUBLIC,Modifier.STATIC)
                        .returns(void.class)
                        .addParameter(String.class, "name")
                        .addStatement("$T.out.println($S)", System.class, "hello")
                        .build();
                TypeSpec myClass = TypeSpec.classBuilder("HelloWord")
                        .addModifiers(Modifier.PUBLIC)
                        .addMethod(spec)
                        .build();
                try {
                    JavaFile javaFile = JavaFile.builder("com.example.startstudy.activity", myClass)
                            .addFileComment("这段代码是自动生成的不要修改!")
                            .build();
                    javaFile.writeTo(mFiler);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return true;
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return Collections.singleton(HelloAnnotation.class.getCanonicalName());
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }
}

(4)使用注解并调用生成的类函数

@HelloAnnotation
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        HelloWorld.setName("111");
    }
}

未编译前,HelloWorld.java是不存在的,这里会报错。那么,我们尝试编译一下,就会发现HelloWorld.java会自动生成,如下:


企业微信截图_15858806138763.png
// 这段代码是自动生成的不要修改!
package com.example.startstudy.activity;

import java.lang.String;
import java.lang.System;

public class HelloWord {
  public static void setName(String name) {
    System.out.println("hello");
  }
}

这样就完成了。

知识储备:
一、注解处理器(Annotation Processor)
注解处理器(Annotation Processor)是javac的一个工具,它用来在编译时扫描和处理注解(Annotation)。你可以自定义注解,并注册相应的注解处理器(自定义的注解处理器需继承自AbstractProcessor)。

public class MyProcessor extends AbstractProcessor {

    /**
     * 每一个注解处理器类都必须有一个空的构造函数。
     * 然而,这里有一个特殊的init()方法,它会被注解处理工具调用,并输入processingEnvironment参数。
     * @param processingEnvironment 提供很多有用的工具类如Elements, Types和Filer等。
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
    }

    /**
     * 这相当于每个处理器的主函数main()。
     * 你在这里写你的扫描、评估和处理注解的代码,以及生成Java文件。
     * 输入参数roundEnvironment,可以让你查询出包含特定注解的被注解元素。
     */
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        return false;
    }

    /**
     * 这里你必须指定,这个注解处理器是注册给哪个注解的。
     * 注意,它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称。
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return super.getSupportedAnnotationTypes();
    }

    /**
     * 用来指定你使用的Java版本。
     * 通常这里返回SourceVersion.latestSupported()。
     */
    @Override
    public Set<String> getSupportedOptions() {
        return super.getSupportedOptions();
    }
}

二、com.google.auto.service:auto-service
Google提供了一个插件来帮助我们更方便的注册注解处理器,你只需要导入对应的依赖包,在自定义的Processor类上方添加@AutoService(Processor.class)即可。

三、com.neenbedankt.android-apt
该插件用于处理注解处理器

小结

JavaPoet为square出品,并且诸如butterknife、Dagger等著名开源框架也使用该库,可见其质量保障性和稳定性。 JavaPoet提供的api清晰明了,使用起来简单方便,功能方面也很齐全,发布了很久目前也已迭代了很多个版本,趋于稳定阶段。 运用JavaPoet预生成代码的方式,在省去我们频繁书写重复代码的同时,也避免了使用运行时反射造成的效率问题。

参考资料:

上一篇 下一篇

猜你喜欢

热点阅读