美文网首页Android开发Android开发经验谈Android技术知识
JavaPoet + 注解 +注解处理器 优雅的生成代码

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

作者: 大漠孤烟直_v | 来源:发表于2020-04-07 14:36 被阅读0次

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

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

    使用场景

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

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

    工程目录结构:

    • app
    • annotations (注解相关)
    • annotation_compiler (处理器生成代码相关)

    (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预生成代码的方式,在省去我们频繁书写重复代码的同时,也避免了使用运行时反射造成的效率问题。

    参考资料:

    相关文章

      网友评论

        本文标题:JavaPoet + 注解 +注解处理器 优雅的生成代码

        本文链接:https://www.haomeiwen.com/subject/icsmjhtx.html