ARouter讲解2-AutowiredProcessor

  1. ARouter讲解1-InterceptorProcessor

  2. ARouter讲解2-AutowiredProcessor

Autowired

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface Autowired {

    // Mark param's name or service name.
    String name() default "";

    // If required, app will be crash when value is null.
    // Primitive type wont be check!
    boolean required() default false;

    // Description of the field
    String desc() default "";
}

概述

Autowired的对用是对字段进行注入,要不然就要用 getIntent().getIntExtra()之类的方法。这也会生成一个新的类,注入的操作也就在这个类中操作。就如同类似下面的类

image-20210511230216155.png

详解

建议先看ARouter讲解1-InterceptorProcessor,因为这里讲解的比较详细,比如Element、TypeMirror是什么等,都在第一篇文章中讲了,这里就不再讲了。

// Autowired 注解用在字段上,字段在类或者接口里,这里的HashMap中的key就是类或者接口,value是这个类里被Autowired注解的元素
private Map<TypeElement, List<Element>> parentAndChild = new HashMap<>();


public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
    if (CollectionUtils.isNotEmpty(set)) {
        try {
            this.categories(roundEnvironment.getElementsAnnotatedWith(Autowired.class));
            this.generateHelper();
        } catch (Exception var4) {
            this.logger.error(var4);
        }

        return true;
    } else {
        return false;
    }
}


// 方法里的 elements 参数是字段元素,是字段一般都是在类或者接口里面
private void categories(Set<? extends Element> elements) throws IllegalAccessException {
        if (CollectionUtils.isNotEmpty(elements)) {
            for (Element element : elements) {
                // 获取这个字段所在的类或接口
                TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
                // 该类或者接口不能被 private 修饰
                if (element.getModifiers().contains(Modifier.PRIVATE)) {
                    throw new IllegalAccessException("The inject fields CAN NOT BE 'private'!!! please check field ["
                            + element.getSimpleName() + "] in class [" + enclosingElement.getQualifiedName() + "]");
                }
                
                if (parentAndChild.containsKey(enclosingElement)) { // Has categries
                    parentAndChild.get(enclosingElement).add(element);
                } else {
                    List<Element> childs = new ArrayList<>();
                    childs.add(element);
                    parentAndChild.put(enclosingElement, childs);
                }
            }

            logger.info("categories finished.");
        }
    }

image-20210511215138090.png

parentAndChild就是收集类中所有被Autowired 注解的字段。

package com.alibaba.android.arouter.facade.template;

public interface ISyringe {
    void inject(Object target);
}
private void generateHelper() throws IOException, IllegalAccessException {

    // 先准备一些element
    // com.alibaba.android.arouter.facade.template.ISyringe
    TypeElement type_ISyringe = elementUtils.getTypeElement(ISYRINGE);
    // com.alibaba.android.arouter.facade.service.SerializationService  这个是一个 IProvider ,我们后面讲
    TypeElement type_JsonService = elementUtils.getTypeElement(JSON_SERVICE);
    
    // com.alibaba.android.arouter.facade.template.IProvider
    TypeMirror iProvider = elementUtils.getTypeElement(Consts.IPROVIDER).asType();
    
    // android.app.Activity
    TypeMirror activityTm = elementUtils.getTypeElement(Consts.ACTIVITY).asType();
    
    // android.app.Fragment
    TypeMirror fragmentTm = elementUtils.getTypeElement(Consts.FRAGMENT).asType();
    
    // android.support.v4.app.Fragment
    TypeMirror fragmentTmV4 = elementUtils.getTypeElement(Consts.FRAGMENT_V4).asType();

    
    // Build input param name.
    // 方法的参数类型与参数名
    ParameterSpec objectParamSpec = ParameterSpec.builder(TypeName.OBJECT, "target").build();

    if (MapUtils.isNotEmpty(parentAndChild)) {
        // 遍历一个每一个类内部带有Autowired 注解的字段
        for (Map.Entry<TypeElement, List<Element>> entry : parentAndChild.entrySet()) {
            // Build method : 'inject'
            // 构建一个方法,public void inject(Object target)
            MethodSpec.Builder injectMethodBuilder = MethodSpec.methodBuilder(METHOD_INJECT)
                    .addAnnotation(Override.class)
                    .addModifiers(PUBLIC)
                    .addParameter(objectParamSpec);

            TypeElement parent = entry.getKey();
            List<Element> childs = entry.getValue();

            // 该类的全限定名
            String qualifiedName = parent.getQualifiedName().toString();
            // 类的包名
            String packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf("."));
            // 类的名字,注意 生成薪的类的后面追加了 $$ARouter$$Autowired
            String fileName = parent.getSimpleName() + NAME_OF_AUTOWIRED;


            // 构建一个类,实现 ISyringe 接口,访问方式是public
            TypeSpec.Builder helper = TypeSpec.classBuilder(fileName)
                    .addJavadoc(WARNING_TIPS)
                    .addSuperinterface(ClassName.get(type_ISyringe))
                    .addModifiers(PUBLIC);

            // 添加一个成员变量 serializationService
            FieldSpec jsonServiceField = FieldSpec.builder(TypeName.get(type_JsonService.asType()), "serializationService", Modifier.PRIVATE).build();
            helper.addField(jsonServiceField);

            // 在inject 方法内部
            // serializationService = ARouter.getInstance().navigation(SerializationService.class);
            // serializationService 的作用是序列化对象,将对象转换成json进行传递
            injectMethodBuilder.addStatement("serializationService = $T.getInstance().navigation($T.class)", ARouterClass, ClassName.get(type_JsonService));
            injectMethodBuilder.addStatement("$T substitute = ($T)target", ClassName.get(parent), ClassName.get(parent));

            // Generate method body, start inject.
            for (Element element : childs) {
                Autowired fieldConfig = element.getAnnotation(Autowired.class);
                // 元素的名字
                String fieldName = element.getSimpleName().toString();
                
                if (types.isSubtype(element.asType(), iProvider)) {  // It's provider
                    // 如果 element 是 IProvider 的子类
                    if ("".equals(fieldConfig.name())) {    // User has not set service path, then use byType.

                        // Getter
                        //  substitute.{fieldName} = ARouter.getInstance().navigation( {elementClass}.class),{}表示占位
                        injectMethodBuilder.addStatement(
                                "substitute." + fieldName + " = $T.getInstance().navigation($T.class)",
                                ARouterClass,
                                ClassName.get(element.asType())
                        );
                    } else {    // use byName
                        // Getter
                          //  substitute.{fieldName} =({elementClass}) ARouter.getInstance().build({ConfigName}).navigation()
                        injectMethodBuilder.addStatement(
                                "substitute." + fieldName + " = ($T)$T.getInstance().build($S).navigation()",
                                ClassName.get(element.asType()),
                                ARouterClass,
                                fieldConfig.name()
                        );
                    }

                    // Validator
                    if (fieldConfig.required()) {
                        // 如果 required 是true,注入后需要检查是不是null
                        injectMethodBuilder.beginControlFlow("if (substitute." + fieldName + " == null)");
                        injectMethodBuilder.addStatement(
                                "throw new RuntimeException(\"The field '" + fieldName + "' is null, in class '\" + $T.class.getName() + \"!\")", ClassName.get(parent));
                        injectMethodBuilder.endControlFlow();
                    }
                } else {    // It's normal intent value
                   
                    String originalValue = "substitute." + fieldName;
                    String statement = "substitute." + fieldName + " = " + buildCastCode(element) + "substitute.";
                    boolean isActivity = false;
                    if (types.isSubtype(parent.asType(), activityTm)) {  // Activity, then use getIntent()
                        // 类似 这样   substitute.age = substitute.getIntent().getIntExtra("age", substitute.age);
                        isActivity = true;
                        statement += "getIntent().";
                    } else if (types.isSubtype(parent.asType(), fragmentTm) || types.isSubtype(parent.asType(), fragmentTmV4)) {   // Fragment, then use getArguments()
                        // 类似  substitute.name = substitute.getArguments().getString("name", substitute.name);
                        statement += "getArguments().";
                    } else {
                        throw new IllegalAccessException("The field [" + fieldName + "] need autowired from intent, its parent must be activity or fragment!");
                    }

                    statement = buildStatement(originalValue, statement, typeUtils.typeExchange(element), isActivity, isKtClass(parent));
                    if (statement.startsWith("serializationService.")) {   // Not mortals
                        injectMethodBuilder.beginControlFlow("if (null != serializationService)");
                        injectMethodBuilder.addStatement(
                                "substitute." + fieldName + " = " + statement,
                                (StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name()),
                                ClassName.get(element.asType())
                        );
                        injectMethodBuilder.nextControlFlow("else");
                        injectMethodBuilder.addStatement(
                                "$T.e(\"" + Consts.TAG + "\", \"You want automatic inject the field '" + fieldName + "' in class '$T' , then you should implement 'SerializationService' to support object auto inject!\")", AndroidLog, ClassName.get(parent));
                        injectMethodBuilder.endControlFlow();
                    } else {
                        injectMethodBuilder.addStatement(statement, StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name());
                    }

                    // Validator
                    if (fieldConfig.required() && !element.asType().getKind().isPrimitive()) {  // Primitive wont be check.
                        injectMethodBuilder.beginControlFlow("if (null == substitute." + fieldName + ")");
                        injectMethodBuilder.addStatement(
                                "$T.e(\"" + Consts.TAG + "\", \"The field '" + fieldName + "' is null, in class '\" + $T.class.getName() + \"!\")", AndroidLog, ClassName.get(parent));
                        injectMethodBuilder.endControlFlow();
                    }
                }
            }

            helper.addMethod(injectMethodBuilder.build());

            // Generate autowire helper
            JavaFile.builder(packageName, helper.build()).build().writeTo(mFiler);

            logger.info(">>> " + parent.getSimpleName() + " has been processed, " + fileName + " has been generated. <<<");
        }

        logger.info(">>> Autowired processor stop. <<<");
    }
}

最后生成的类就类似下面这样

image-20210511223609007.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341