{"id":16790128,"url":"https://github.com/chenlittleping/annotationdemo","last_synced_at":"2025-04-10T23:27:37.385Z","repository":{"id":109497122,"uuid":"101456227","full_name":"ChenLittlePing/AnnotationDemo","owner":"ChenLittlePing","description":"Android／Java编译时注解处理Demo。用于自动生成工厂代码。","archived":false,"fork":false,"pushed_at":"2017-08-26T03:02:40.000Z","size":141,"stargazers_count":29,"open_issues_count":0,"forks_count":8,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-24T20:13:29.853Z","etag":null,"topics":["android","annotation","factory","javapoet","processor"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ChenLittlePing.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-08-26T02:14:07.000Z","updated_at":"2024-08-01T07:47:39.000Z","dependencies_parsed_at":"2023-04-25T04:47:44.207Z","dependency_job_id":null,"html_url":"https://github.com/ChenLittlePing/AnnotationDemo","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChenLittlePing%2FAnnotationDemo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChenLittlePing%2FAnnotationDemo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChenLittlePing%2FAnnotationDemo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChenLittlePing%2FAnnotationDemo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ChenLittlePing","download_url":"https://codeload.github.com/ChenLittlePing/AnnotationDemo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248314157,"owners_count":21083000,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","annotation","factory","javapoet","processor"],"created_at":"2024-10-13T08:29:02.948Z","updated_at":"2025-04-10T23:27:37.368Z","avatar_url":"https://github.com/ChenLittlePing.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"### 写在前面：\n越来越多的Android框架都使用了注解来实现，如有名ButterKnife、Dagger2都是用编译时注解来生成代码，好处是比反射效率更高，稳定性、可读性也更好。既然注解这么好用，那么就非常有必要对其进行了解、学习和应用。\n\n学习注解过程中，查找了很多人分享的文章，非常感谢这些无私分享的人。其中参考了比较多的是[这篇文章](https://www.race604.com/annotation-processing/)，本文中的例子也是参考该文章，并结合自己对注解的理解，重新写了本文中的Demo，加入更详细的注释。\n\n本文是本人在学习注解时，对注解的理解和一些基础知识的记录所写，仅仅作为入门，分享给需要的小伙伴们。可能存在一些疏漏和错误，欢迎指正～\n\n### 一、Java注解基础：\n在Java中，一个自定义的注解看起来是类似下面这样子的：\n\n```java\n@Retention(RetentionPolicy.CLASS)\n@Target(ElementType.TYPE)\npublic @interface Factory {\n    String value() default \"\";\n}\n```\n\n该注解用于编译时使用，生命周期由@Retention指定，@Taget表示该注解的使用范围，这里用于注解类、接口、枚举。\n\n那么，@Retention和@Target是什么东东？\n\n#### 元注解：\n\n元注解的作用就是负责注解其他非元注解。Java5.0定义了4个标准的meta-annotation类型，它们被用来提供对其它 Annotation类型作说明。\n#### Java5.0定义的元注解：\n\n* @Target\n* @Retention\n* @Documented\n* @Inherited\n\n##### 1. @Target\n  \n    作用：用于描述注解的使用范围（即：被描述的注解可以用在什么地方）\n   \n取值(ElementType)有：\n\n* CONSTRUCTOR:用于描述构造器\n* FIELD:用于描述域\n* LOCAL_VARIABLE:用于描述局部变量\n* METHOD:用于描述方法\n* PACKAGE:用于描述包\n* PARAMETER:用于描述参数\n* TYPE:用于描述类、接口(包括注解类型) 或enum声明\n\n##### 2. @Retention\n    \n    作用：表示需要在什么级别保存该注释信息，用于描述注解的生命周期（即：被描述的注解在什么范围内有效）\n\n取值（RetentionPoicy）有：\n\n* SOURCE:在源文件中有效（即源文件保留，只在源文件中，如@Override）\n* CLASS:在class文件中有效（即class保留，可在编译时获取，本文主讲内容）\n* RUNTIME:在运行时有效（即运行时保留,可在运行是通过反射获取）\n\n##### 3.@Documented:\n\n    @Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API，  \n    因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解，没有成员。\n    \n##### 4.@Inherited：\n\n    @Inherited 元注解是一个标记注解，@Inherited阐述了某个被标注的类型是被继承的。  \n    如果一个使用了@Inherited修饰的annotation类型被用于一个class，则这个annotation将被用于该class的子类。  \n    使用Inherited声明出来的注解，只有在类上使用时才会有效，对方法，属性等其他无效。\n\n#### 自定义注解\n\n##### 格式：public @interface 注解名 {定义体}\n\n##### 注解参数的可支持数据类型：\n\n1. 所有基本数据类型（int,float,boolean,byte,double,char,long,short)\n2. String类型\n3. Class类型\n4. enum类型\n5. Annotation类型\n6. 以上所有类型的数组\n\n\u003e参数职能用public或默认(default)修饰\n\n\u003e如果只有一个参数成员,最好把参数名称设为\"value\",后加小括号,即value()\n\n### 二、在Android中应用编译时注解，自动生成工厂代码\n\n首先以工厂模式为例，看看在工厂模式中存在的问题。本例假设为水果工厂。\n\n1.通常，在工厂模式中，我们会定义一个工厂生产接口方法：\n\n```Java\npublic interface IFruit {\n    void produce();\n}\n```\n2.接着，定义具体的工厂生产线类：\n\n```Java\npublic class Apple implements IFruit {\n    @Override\n    public void produce() {\n        Log.d(\"AnnotationDemo\", \"生产苹果\");\n    }\n}\n\npublic class Pear implements IFruit {\n    @Override\n    public void produce() {\n        Log.d(\"AnnotationDemo\", \"生产梨子\");\n    }\n}\n\n```\n\n3.然后，定义生产工厂类：\n\n```Java\npublic class FruitFactory {\n    public static IFruit create(int id) {\n        if (1 == id) {\n            return new Apple();\n        }\n        if (2 == id) {\n            return new Pear();\n        }\n        \n        return null;\n    }\n}\n```\n\n4.最后，使用工厂：\n\n```Java\npublic void produceFruit() {\n    FruitFactory.create(1).produce();\n    FruitFactory.create(2).produce();\n}\n```\n\n* 存在问题：\n\n    在以上例子中，每次新增生产线的时候，都需要先定义一个生产线，然后在FruitFactory的create方法中新增判断，返回新的生产线类，并且每次添加的代码都是非常相似重复的。\n\n    为此，“懒惰”的我们肯定会想，是否有方法可以做到：只要我定义好一个生产线类后，无需手动地在工厂类中添加，就马上可以使用？\n    \n    答案是肯定的，Java的注解处理器（AbstractProcessor）就可以帮助我们实现以上需求。\n    \n接下来，我们就一步步来实现这个可以让我们懒出新境界的功能：\n#### 1. 新建Android工程和Java Module\n\u003e 注意：由于Android默认不支持部分javax包的内容，所以我们需要将注解解析相关的类放到Java Module中才能调用到。\n* 建立好Android工程 **AnnotationDemo**\n* 新建annotator Module ：Filw -\u003e New -\u003e New Module -\u003e Java Library 并命名为**annotator**\n\n#### 2. 配置APT(Annotation Processor Tool)工具。\n\u003e 由于android-apt已经不再维护，并且Android官方在Gradle2.2以上已经提供了另一个工具annotationProcessor替代了原来的android-apt，所以我们直接使用annotationProcessor。\nGradle2.2以下版本配置请看最后。\n\n在app的build.gradle中添加如下依赖：\n\n```\ndependencies {\n    ......\n    \n    compile project(':annotator')\n    annotationProcessor project(':annotator')\n}\n```\n\n#### 3. 码注解处理器\n\u003e 以上配置完成后，就可以开始码注解处理器了。\n\n1）首先，自定义一个注解，用于标识生产线类，该注解包含两个参数:\n* 一个生产线类id数组ids，可多个id对应一个类\n* 另一个是该生产类的接口父类，用于标识生产线类的接口父类\n\n```Java\n@Retention(RetentionPolicy.CLASS) //该注解只保留到编译时\n@Target(ElementType.TYPE) //该注解只作用与类、接口、枚举\npublic @interface Factory {\n    /**\n     * 工厂对应的ID，可以多个ID对应一个生产线类\n     */\n    int[] ids();\n\n    /**\n     * 生产接口类\n     */\n    Class superClass();\n}\n```\n\n#### 2）使用以上注解标记生产线类\n\n```Java\n@Factory(ids = {1}, superClass = IFruit.class)\npublic class Apple implements IFruit {\n    @Override\n    public void produce() {\n        Log.d(\"AnnotationDemo\", \"生成苹果\");\n    }\n}\n\n@Factory(ids = {2,3}, superClass = IFruit.class)\npublic class Pear implements IFruit {\n    @Override\n    public void produce() {\n        Log.d(\"AnnotationDemo\", \"生成梨子\");\n    }\n}\n```\n\n以上Pear类上，我们使用了Factory注解标记，其中参数ids有两个id，即使用2或者3都可以获取到Pear；superClass为生产接口类。\n\n#### 3）编写注解解析器\n\n* ***i. 首先，定义一个注解属性类，用于保存获取到的每个生产线类相关的属性***\n\n```Java\npublic class FactoryAnnotatedCls {\n    private TypeElement mAnnotatedClsElement; //被注解类元素\n\n    private String mSupperClsQualifiedName; //被注解的类的父类的完全限定名称（即类的绝对路径）\n\n    private String mSupperClsSimpleName; //被注解类的父类类名\n\n    private int[] mIds; //被注解的类的对应的ID数组\n\n\n    public FactoryAnnotatedCls(TypeElement classElement) throws ProcessingException {\n        this.mAnnotatedClsElement = classElement;\n        Factory annotation = classElement.getAnnotation(Factory.class);\n        mIds = annotation.ids();\n        try {\n            //直接获取Factory中的supperClass参数的类名和完全限定名字，如果是源码上的注解，会抛异常\n            mSupperClsSimpleName = annotation.superClass().getSimpleName();\n            mSupperClsQualifiedName = annotation.superClass().getCanonicalName();\n        } catch (MirroredTypeException mte) {\n            //如果获取异常，通过mte可以获取到上面无法解析的superClass元素\n            DeclaredType classTypeMirror = (DeclaredType) mte.getTypeMirror();\n            TypeElement classTypeElement = (TypeElement) classTypeMirror.asElement();\n            mSupperClsQualifiedName = classTypeElement.getQualifiedName().toString();\n            mSupperClsSimpleName = classTypeElement.getSimpleName().toString();\n        }\n\n        if (mIds == null || mIds.length == 0) { //判断是否存在ID，不存在则抛出异常\n            throw new ProcessingException(classElement,\n                    \"id() in @%s for class %s is null or empty! that's not allowed\",\n                    Factory.class.getSimpleName(), classElement.getQualifiedName().toString());\n        }\n\n        if (mSupperClsSimpleName == null || mSupperClsSimpleName == \"\") { //判断是否存在父类接口，不存在抛出异常\n            throw new ProcessingException(classElement,\n                    \"superClass() in @%s for class %s is null or empty! that's not allowed\",\n                    Factory.class.getSimpleName(), classElement.getQualifiedName().toString());\n        }\n    }\n\n    public int[] getIds() {\n        return mIds;\n    }\n\n    public String getSupperClsQualifiedName() {\n        return mSupperClsQualifiedName;\n    }\n\n    public String getSupperClsSimpleName() {\n        return mSupperClsSimpleName;\n    }\n\n    public TypeElement getAnnotatedClsElement() {\n        return mAnnotatedClsElement;\n    }\n}\n```\n\n其中，有个类为TypeElement，该类继承Element。程序编译时，IDE扫描文件所有的属性都可以被看作元素。继承自Element的子类共有四个，分别为：\n\n* TypeElement （类属性元素，对应一个类）\n* PackageElement （包元素，对应一个包）\n* VariableElement （变量元素，对应变量）\n* ExecuteableElement （方法元素，对应函数方法）\n\n在这里，定义的注解目标是Type，因此为TypeElement。FactoryAnnotatedCls类将被Factory注解的类中的必要属性都保存下来，用于后面生成代码。\n\n* ***ii. 接下来，是解析注解代码的关键类：注解处理器***\n\n所有在编译时处理注解的程序，都需要定义一个注解处理器，继承自AbstractProcessor。\n\n```Java\n@AutoService(Processor.class)\npublic class FactoryProcesser extends AbstractProcessor {\n\n    private Types mTypeUtil;\n    private Elements mElementUtil;\n    private Filer mFiler;\n    private Messager mMessager;\n\n    private FactoryCodeBuilder mFactoryCodeBuilder = new FactoryCodeBuilder();\n\n    @Override\n    public synchronized void init(ProcessingEnvironment processingEnvironment) {\n        super.init(processingEnvironment);\n        mTypeUtil = processingEnvironment.getTypeUtils();\n        mElementUtil = processingEnvironment.getElementUtils();\n        mFiler = processingEnvironment.getFiler();\n        mMessager = processingEnvironment.getMessager();\n    }\n    \n    @Override\n    public Set\u003cString\u003e getSupportedAnnotationTypes() {\n        Set\u003cString\u003e annotations = new LinkedHashSet\u003c\u003e();\n        annotations.add(Factory.class.getCanonicalName());\n        return annotations;\n    }\n\n    @Override\n    public SourceVersion getSupportedSourceVersion() {\n        return SourceVersion.latestSupported();\n    }\n\n    ......\n}\n```\n\n其中，\n\ngetSupportedAnnotationTypes()配置需要处理的注解，这里只处理@Factory注解；  \ngetSupportedSourceVersion()配置支持的Java版本\n\ninit()方法中，获取了几个即将用到的工具：  \nmTypeUtil--主要用于获取类  \nmElementUtil--主要用于解析各种元素  \nmFiler--用于写文件，生成代码  \nmMessager--用于在控制台输出信息\n\n另外，在第一个行代码中，有一个注解**AutoService(Processor.class)**。这个注解的作用是可以自动生成javax.annotation.processing.Processor文件。该文件位于\"build/classes/main/com/META-INF/services/\"中。  \n\n文件中只有一句话，配置了注解处理器的完全限定名。\n\n```\ncom.factorybuilder.FactoryProcesser\n```\n\n当然，需要在annotator Module的build.gradle添加依赖才能使用AutoService注解。\n\n```\ncompile 'com.google.auto.service:auto-service:1.0-rc2'\n```\n\n**注：只有在该文件配置了的注解处理器，在编译时才会被调用。**\n\n完成以上配置后，就可以进入注解的解析和处理了。在编译时，编译器将自动调用注解处理器的process方法。如下：\n\n```Java\n@AutoService(Processor.class)\npublic class FactoryProcesser extends AbstractProcessor {\n    ......\n    \n    @Override\n    public boolean process(Set\u003c? extends TypeElement\u003e set, RoundEnvironment roundEnvironment) {\n        for (Element annotatedElement : roundEnvironment.getElementsAnnotatedWith(Factory.class)) { //遍历所有被Factory注解的元素\n                if (annotatedElement.getKind() != ElementKind.CLASS) { //判断是否为类，如果不是class，抛出异常\n                    error(annotatedElement,\n                          String.format(\"Only class can be annotated with @%s\",\n                                Factory.class.getSimpleName()));\n                }\n\n                TypeElement typeElement = (TypeElement) annotatedElement; //将元素转换为TypeElement（因为在上面的代码中，已经判断了元素为class类型）\n                FactoryAnnotatedCls annotatedCls = new FactoryAnnotatedCls(typeElement); //接着将该元素保存到先前定义的类中\n                supperClsPath = annotatedCls.getSupperClsQualifiedName().toString(); //获取元素的父类路径（在这里为IFruit）\n\n                checkValidClass(annotatedCls);//检查元素是否符合规则\n\n                mFactoryCodeBuilder.add(annotatedCls); //将元素压入列表中，等待最后用于生成工厂代码\n        }\n    \n        if (supperClsPath != null \u0026\u0026 !supperClsPath.equals(\"\")) { //检查是否有父类路径\n            mFactoryCodeBuilder\n            .setSupperClsName(supperClsPath)\n            .generateCode(mMessager, mElementUtil, mFiler); //开始生成代码\n        }\n    \n        return true; //return true表示处理完毕\n    }\n}\n```\n\n在process方法中，  \n**首先**，遍历了所有被Factory标记的元素；  \n**然后**，对每一个元素进行检查，如果为class类型，并且符合指定的规则，统统压入FactoryCodeBuilder的列表中；  \n**最后**，如果所有的元素都符合规则，调用factoryCodeBuilderd的generateCode生成代码。\n\n* ***iii. 最后，来看看FacrotyCodeBuilder都做了些什么***\n\n```Java\npublic class FactoryCodeBuilder {\n\n    private static final String SUFFIX = \"Factory\";\n\n    private String mSupperClsName;\n\n    private Map\u003cString, FactoryAnnotatedCls\u003e mAnnotatedClasses = new LinkedHashMap\u003c\u003e();\n\n    public void add(FactoryAnnotatedCls annotatedCls) {\n        if (mAnnotatedClasses.get(annotatedCls.getAnnotatedClsElement().getQualifiedName().toString()) != null)\n            return ;\n\n        mAnnotatedClasses.put(\n                annotatedCls.getAnnotatedClsElement().getQualifiedName().toString(),\n                annotatedCls);\n    }\n\n    public void clear() {\n        mAnnotatedClasses.clear();\n    }\n    \n    ......\n}\n```\n\n代码生成器中定义了一个哈希列表，用于保存所有遍历到的符合规则的元素。\n\n```Java\npublic class FactoryCodeBuilder {\n\n    ......\n\n\n    public FactoryCodeBuilder setSupperClsName(String supperClsName) {\n        mSupperClsName = supperClsName; //设置上产线接口父类的路径\n        return this;\n    }\n\n    public void generateCode(Messager messager, Elements elementUtils, Filer filer) throws IOException {\n        TypeElement superClassName = elementUtils.getTypeElement(mSupperClsName); //通过Elements工具获取父类元素\n        String factoryClassName = superClassName.getSimpleName() + SUFFIX; //然后设置即将生成的工厂类的名字（在这里为IFruitFactory）\n        PackageElement pkg = elementUtils.getPackageOf(superClassName); //通过Elements工具，获取父类所在包名路径（在这里为annotation.demo.factorys）\n        String packageName = pkg.isUnnamed() ? null : pkg.getQualifiedName().toString(); //获取即将生成的工厂类的包名\n\n        TypeSpec typeSpec = TypeSpec\n                .classBuilder(factoryClassName)\n                .addModifiers(Modifier.PUBLIC)\n                .addMethod(newCreateMethod(elementUtils, superClassName))\n                .addMethod(newCompareIdMethod())\n                .build();\n\n        // Write file\n        JavaFile.builder(packageName, typeSpec).build().writeTo(filer);\n    }\n    \n    ......\n}\n```\n\n在generateCode方法中，获取了生产线父类的名称和包名，以及为即将生成的工厂类设置了包名和类名。\n\n然后借助了一个非常厉害的工具JavaPoet。这个工具是由square公司提供的，用于优雅地生成Java代码，如其名字“会写Java的诗人”。  \n在annotator build.gradle中添加依赖：\n\n```\ncompile 'com.squareup:javapoet:1.7.0'\n```\n\n简单介绍一下JavaPoet的用法：  \n\n* **TypeSpec**用于创建类、接口或者枚举  \n调用**classBuilder**设置类名；  \n调用**addModifiers**可以设置类的属性类型，public static final等，可以同时添加多个属性  \n调用**addMethod**可以在类中添加一个函数方法\n* **JavaFile**将创建的类写入文件中\n* **MethodSpec**接下来即将用到的，用于创建函数方法,其使用参考下面代码注释  \n\n更详细用法请自行google，有很多的文章可以查阅。\n\n本例中，给工厂类生成了两个方法分别为\n\n```Java\npublic static IFruit create(int id)\nprivate static compareId(int[] ids, id)\n```\n\n具体代码如下：\n\n```Java\npublic class FactoryCodeBuilder {\n\n    ......\n    \n    private MethodSpec newCreateMethod(Elements elementUtils, TypeElement superClassName) {\n\n        MethodSpec.Builder method =\n                MethodSpec.methodBuilder(\"create\") //设置方法名字\n                .addModifiers(Modifier.PUBLIC, Modifier.STATIC) //设置方法类型为public static\n                .addParameter(int.class, \"id\") //设置参数int id\n                .returns(TypeName.get(superClassName.asType())); //设置返回IFruit\n\n        method.beginControlFlow(\"if (id \u003c 0)\") //beginControlFlow与endControlFlow要成对调用\n                .addStatement(\"throw new IllegalArgumentException($S)\", \"id is less then 0!\")\n                .endControlFlow();\n\n        for (FactoryAnnotatedCls annotatedCls : mAnnotatedClasses.values()) { //遍历所有保存起来的被注解的生产线类\n            String packName = elementUtils\n                    .getPackageOf(annotatedCls.getAnnotatedClsElement())\n                    .getQualifiedName().toString(); //获取生产线类的包名全路径\n            String clsName = annotatedCls.getAnnotatedClsElement().getSimpleName().toString(); //获取生产线类名字\n            ClassName cls = ClassName.get(packName, clsName); //组装成一个ClassName\n\n            //将该生产线类的所有id组成数组\n            int[] ids = annotatedCls.getIds();\n            String allId = \"{\";\n            for (int id : ids) {\n                allId = allId + (allId.equals(\"{\")? \"\":\",\") + id;\n            }\n            allId+=\"}\";\n\n            method.beginControlFlow(\"if (compareId(new int[]$L, id))\", allId) //开始一个控制流，判断该生产线类是否包含了指定的id\n                    .addStatement(\"return new $T()\", cls)   // $T 替换类名，可以自动import对应的类。还有以下占位符：\n                                                            // $N 用于方法名或者变量名替换，也可用于类名，但是不会自动生成import；\n                                                            // $L 字面量替换，如上面if中allId的值替换；；\n                                                            // $S 为替换成String\n                    .endControlFlow();\n        }\n\n        method.addStatement(\"throw new IllegalArgumentException($S + id)\", \"Unknown id = \");\n\n        return method.build();\n    }\n\n    private MethodSpec newCompareIdMethod() {\n        MethodSpec.Builder builder = MethodSpec.methodBuilder(\"compareId\") //设置函数方法名字\n                .addModifiers(Modifier.PRIVATE, Modifier.STATIC) //设置方法类型为private static\n                .addParameter(int[].class, \"ids\") //设置参数int[] ids\n                .addParameter(int.class, \"id\") //设置参数int id\n                .returns(TypeName.BOOLEAN); //设置返回类型\n\n        builder.beginControlFlow(\"for (int i : ids)\") //开始一个控制流\n                .beginControlFlow(\"if (i == id)\") //在以上for循环中加入一个if控制流\n                .addStatement(\"return true\") //添加一行代码，最后会自动添加分号\";\"\n                .endControlFlow() //结束一个控制流，add和end要成对调用。这里对应if的控制流\n                .endControlFlow() //结束for控制流\n                .addStatement(\"return false\"); //按添加返回\n\n        return builder.build();\n    }\n}\n```\n\n以上代码创建了两个方法，一个对外的create方法和内部使用的compareId方法。 \n \n* 在newCreateMethod中，首先创建了create(int id)方法，然后在里面用for循环遍历所有的生产线类，并生成了对应的判断和返回,最终生成类似如下代码：\n\n```Java\npublic static IFruit create(int id) {\n    if(compareId(new int[]{1},id)) {\n        return new Apple();\n    }\n    if(compareId(new int[]{2,3},id)) {\n        return new Pear();\n    }\n}\n```\n\n* 在newCompareIdMethod中，生成了compareId方法，并生了判断输入id与生产线ID匹配的方法，生成类似如下代码：\n\n```Java\nprivate static boolean compareId(int[] ids, int id) {\n    for (int i : ids) {\n      if (i == id) {\n        return true;\n      }\n    }\n    return false;\n  }\n```\n\n至此，一个自动生成工厂类的注解工具就封装完成了。当然，在执行process过程中，还会对元素做一些判断，具体就不做介绍了，需要可以直接看[源码](https://github.com/ChenLittlePing/AnnotationDemo)。\n\n如何使用该工具呢？如新增一个Orange生产线类型。\n\n在app Mudule中的新建Orange如下：\n\n```Java\n@Facroty(ids = {5}, superClass = IFruit.class)\npublic class Orange implement IFruit {\n    @Override\n    public void produce () {\n        Log.d(\"AnnotationDemo\", \"生成橙子\");\n    }\n}\n```\n\nBuild一下工程，就可以直接使用了，简直不能再爽，哈哈哈～\n\n```Java\nprivate void produceFruit() {\n    IFruitFactory.create(5).produce();\n}\n```\n\n最后，看下自动生成的工厂类,跟手写的基本是一样的（该类位于app/build/generated/source/apt/debug/接口父类包名）：\n\n```Java\npackage annotation.demo.factorys;\n\npublic class IFruitFactory {\n  public static IFruit create(int id) {\n    if (id \u003c 0) {\n      throw new IllegalArgumentException(\"id is less then 0!\");\n    }\n    if (compareId(new int[]{1}, id)) {\n      return new Apple();\n    }\n    if (compareId(new int[]{4,5}, id)) {\n      return new Orange();\n    }\n    if (compareId(new int[]{2,3}, id)) {\n      return new Pear();\n    }\n    if (compareId(new int[]{6}, id)) {\n      return new Persimmon();\n    }\n    throw new IllegalArgumentException(\"Unknown id = \" + id);\n  }\n\n  private static boolean compareId(int[] ids, int id) {\n    for (int i : ids) {\n      if (i == id) {\n        return true;\n      }\n    }\n    return false;\n  }\n}\n\n```\n\n以上代码中为了方便讲解省略了一些判断和异常处理，具体可以查看[源码](https://github.com/ChenLittlePing/AnnotationDemo)。\n\n------------------------------------==正文End : ) **我是分割线**==----------------------------------\n\n\n#### gradle2.2以下版本配置\n* 由于Android不完全支持Java8，可能会导致编译报错，所以设置Java版本为Java7。\n\n    1）在app的build.gradle的android标签中添加如下配置\n    \n    ```\n    compileOptions {\n       sourceCompatibility JavaVersion.VERSION_1_7\n       targetCompatibility JavaVersion.VERSION_1_7\n    }\n    ```\n    \n    2）在annotator的build.gradle中配置\n    \n    ```\n    sourceCompatibility = 1.7\n    targetCompatibility = 1.7\n    ```\n    \n* 配置APT\n    \n    1）在项目的build.gradle dependencies添加apt插件：\n    \n    ```\n    dependencies {\n        classpath 'com.android.tools.build:gradle:2.3.3'\n        // apt\n        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'\n    }\n    ```\n    \n    2）在app build.gradle最上面添加\n    \n    ```\n    apply plugin: 'com.neenbedankt.android-apt'\n    ```\n    \n* 配置annotator build.gradle依赖在dependencies中添加依赖\n    \n    ```\n    dependencies {\n        ......\n        compile project(':annotator')\n        apt project(':annotator')\n    }\n    ```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchenlittleping%2Fannotationdemo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchenlittleping%2Fannotationdemo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchenlittleping%2Fannotationdemo/lists"}