ARouter 之@Autowired源码分析

@Autowired

在Activity进行数据传递一般都会通过getIntent().putxxx()/getxxx()方法;在Fragment中进行数据传递一般都会通过getArguments().getxxx()方法,如果传递的字段过多,可能会写很多个取值语句。为了简化这些繁琐的步骤,ARouter提供了使用注解的方式来完成参数的注入,这是通过@Autowired来实现的。

定义

先来看看 @Autowired 的定义。

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface Autowired {
    // 从intent中获取名为name()的值
    String name() default "";
    // 如果是非基本元素,且是必须的,会做null校验。
    boolean required() default false;

    String desc() default "";
}
  1. name() 定义了属性获取值的key;
  2. required()定义了当前属性是否是必须的,ARouter会做校验;

使用

使用十分简单,只需要在需要取值的字段上使用 @Autowired 修饰即可。

@Autowired(name = "orderId")
long orderId;

然后运行代码,会发现并没有什么卵用。

逗我呢.png

莫慌,先来看看 build/generated/source/kapt 下生成的文件。

public class MainActivity$$ARouter$$Autowired implements ISyringe {
  private SerializationService serializationService;

  @Override
  public void inject(Object target) {
    serializationService = ARouter.getInstance().navigation(SerializationService.class);
    MainActivity substitute = (MainActivity)target;
    substitute.orderId = substitute.getIntent().getLongExtra("orderId", substitute.orderId);
  }
}

发现赋值操作主要是通过 inject() 方法完成的,那么这个方法在哪里调用的呢?追溯源码会发现这个方法的调用链是酱紫的 ARouter.inject() -> _ARouter.inject() -> AutowiredServiceImpl.autowire()

而且有一个实现了 Application.ActivityLifecycleCallbacks 的类 AutowiredLifecycleCallback.javaonActivityCreated() 中调用了 ARouter.getInstance().inject(activity)

@Deprecated
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public class AutowiredLifecycleCallback implements Application.ActivityLifecycleCallbacks {
    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        ARouter.getInstance().inject(activity);
    }

    // ...
}

但是这个类已经被标记为过时了,所以不考虑这种方式。

那么只能手动去调用 ARouter.getInstance().inject() 。修改完代码,再次运行,很nice。

源码分析

前面说了,它的调用链 ARouter.inject() -> _ARouter.inject() -> AutowiredServiceImpl.autowire() 是这样子的。先来看看 ARouter.inject()

public void inject(Object thiz) {
    _ARouter.inject(thiz);
}

只是简单的调用了 _ARouter.inject(),再看看其源码。

static void inject(Object thiz) {
    // 这里回去路由表里面查找 /arouter/service/autowired 的实现类,就是AutowiredServiceImpl
    AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
    if (null != autowiredService) {
        // 然后调用其 autowire()
        autowiredService.autowire(thiz);
    }
}

这个方法做了两件事情:

  1. 先从路由表中查找名为 /arouter/service/autowired 的类,也就是AutowiredServiceImpl
  2. 然后调用 AutowiredServiceImpl.autowire()

再来看看 AutowiredServiceImpl的源码。

@Route(path = "/arouter/service/autowired")
public class AutowiredServiceImpl implements AutowiredService {
    private LruCache<String, ISyringe> classCache;
    private List<String> blackList;

    @Override
    public void init(Context context) {
        classCache = new LruCache<>(66);
        blackList = new ArrayList<>();
    }

    @Override
    public void autowire(Object instance) {
        String className = instance.getClass().getName();
        try {
            // 不在黑名单
            if (!blackList.contains(className)) {
                // 从缓存中取
                ISyringe autowiredHelper = classCache.get(className);
                if (null == autowiredHelper) {  // No cache.
                    // 如果缓存没有,则通过反射的方式,创建实例
                    autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
                }
                // 调用其inject()方法,就是编译期生成的xxx$$ARouter$$Autowired文件的inject()
                autowiredHelper.inject(instance);
                classCache.put(className, autowiredHelper);
            }
        } catch (Exception ex) {
            // 如果抛异常,则丢到黑名单中
            blackList.add(className); 
        }
    }
}
  1. classCache 用来存放 xx$$ARouter$$Autowired 的反射实例,blackList用来存放不需要注入操作的类;
  2. autowire() 先看当前类是否在黑名单列表,如果不在,则从缓存中获取当前类对应的生成类的实例;
  3. 如果缓存中没有,则通过反射的方式创建生成类的实例,并调用 inject() 方法。

AutowiredProcessor

上面分析完 @Autowired 的源码,接下来再分析一下,xx$ARouter$$Autowired 类是如何生成的?Annotation Processor 主要是通过 AbstractProcessor.process() 方法做的处理,追溯源码,找到 AutowiredProcessor.java,接下来就看看 process() 方法究竟干了什么。

@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
    if (CollectionUtils.isNotEmpty(set)) {
        try {
            // 1. 这里会先将所有使用到了@Autowired的元素进行分类
            categories(roundEnvironment.getElementsAnnotatedWith(Autowired.class));
            // 2. 分类完成之后再进行文件生成操作
            generateHelper();
        } catch (Exception e) {
        }
        return true;
    }
    return false;
}

可以看到 process() 方法分为两步

  1. 先调用 categories() 将元素进行分类;
  2. 然后调用 generateHelper() 对分类后的元素进行处理,生成对应的 java 文件。

再来看看 categories() 方法。

private void categories(Set<? extends Element> elements) throws IllegalAccessException {
    if (CollectionUtils.isNotEmpty(elements)) {
        for (Element element : elements) {
            // 1. 获取元素所在类的 TypeElement 对象
            TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
            // 2. 这里解释了为什么 @Autowired 注解修饰的 field 不能使用 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() + "]");
            }

            // parentAndChild 是一个map,key是内部使用了@Autowired注解的TypeElement,value是这个类下面所有使用了@Autowired注解的Element
            if (parentAndChild.containsKey(enclosingElement)) { 
                parentAndChild.get(enclosingElement).add(element);
            } else {
                List<Element> childs = new ArrayList<>();
                childs.add(element);
                parentAndChild.put(enclosingElement, childs);
            }
        }

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

categories() 方法的逻辑十分的简单,接下来看看 generateHelper() ,这个方法有点长,我们来一段一段的分析。

// 构造参数
ParameterSpec objectParamSpec = ParameterSpec.builder(TypeName.OBJECT, "target").build();

if (MapUtils.isNotEmpty(parentAndChild)) {
    for (Map.Entry<TypeElement, List<Element>> entry : parentAndChild.entrySet()) {
        // 构造方法
        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;
        // 构造方法
        TypeSpec.Builder helper = TypeSpec.classBuilder(fileName)
                .addJavadoc(WARNING_TIPS)
                .addSuperinterface(ClassName.get(type_ISyringe))
                .addModifiers(PUBLIC);
        // 构造属性
        FieldSpec jsonServiceField = FieldSpec.builder(TypeName.get(type_JsonService.asType()), "serializationService", Modifier.PRIVATE).build();
        helper.addField(jsonServiceField);
        // 给方法添加语句
        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));

        // ... 省略后面的代码
}

这里使用的是 squareup 的开源库 javapoet。这个库的使用,大家可以看官方文档,这里就不做介绍了。上面这段代码定义了如下的一个类和方法。

// 定义的类
package packageName;

import com.alibaba.android.arouter.facade.service.SerializationService;

public class 类名$$ARouter$$Autowired implement ISyringe {
    private SerializationService serializationService;
}

// 定义的方法
@Override
public void inject(Object target) {
    serializationService = ARouter.getInstance().navigation(SerializationService.class);
    类名 substitute = (类名)target;
}

继续看后续的代码。

// 这里遍历当前类下所有的@Autowired注解修饰的属性
for (Element element : childs) {
    Autowired fieldConfig = element.getAnnotation(Autowired.class);
    String fieldName = element.getSimpleName().toString();
    if (types.isSubtype(element.asType(), iProvider)) {
          // 1. 如果是 Provider 
    } else {
          // 2. 如果不是 Provider,就正常的Intent方式
    }
}

可以看到在 for 语句中对 Element 的处理主要分为两种;一种是对于 IProvider 的自动注入处理,一种是对于正常 Activity / Fragment 进行数据传递的时候自动注入的处理。

先来看看 IProvider 的自动注入。

if ("".equals(fieldConfig.name())) {
    // 没有指定path的情况                  
    injectMethodBuilder.addStatement(
            "substitute." + fieldName + " = $T.getInstance().navigation($T.class)",
            ARouterClass,
            ClassName.get(element.asType())
    );
} else {
    // 指定了path的情况
    injectMethodBuilder.addStatement(
            "substitute." + fieldName + " = ($T)$T.getInstance().build($S).navigation()",
            ClassName.get(element.asType()),
            ARouterClass,
            fieldConfig.name()
    );
}

// 如果是必须的,做一个校验
if (fieldConfig.required()) {
    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();
}

可以看到,对于 IProvider 的自动注入处理逻辑如下:

  1. 如果 @Autowired 注解没有指定 name(),则通过 Class 来查找对应的 IProvider
  2. 如果指定了 name(),则直接通过路径来查找对应的 IProvider
  3. 如果 require() 返回了 true,则给出校验的逻辑。

接下来我们再看看 Activity / Fragment 数据自动注入是如何处理的。

String originalValue = "substitute." + fieldName;
// buildCastCode 主要是判断是否是 Serializable和Parcelable,然后对其进行类型转换
String statement = "substitute." + fieldName + " = " + buildCastCode(element) + "substitute.";
boolean isActivity = false;
if (types.isSubtype(parent.asType(), activityTm)) {
    // 如果是 Activity,则使用getIntent() 方式
    isActivity = true;
    statement += "getIntent().";
} else if (types.isSubtype(parent.asType(), fragmentTm) || types.isSubtype(parent.asType(), fragmentTmV4)) {   
    // 如果是 Fragment,则使用getArguments() 方式
    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);
if (statement.startsWith("serializationService.")) {
    // 只有当Element对应的类型不是8大基本类型,String,Serializable,Parcelable的时候,才会有这个逻辑
    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 {
    // 构造语句,可以看到当@Autowired注解name()对应的值是空的时候直接使用属性名作为key获取
    injectMethodBuilder.addStatement(statement, StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name());
}

// 如果fieldConfig 是必须的,并且Element 不是8大基本数据类型的时候,加个判断语句
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();
}

buildStatement() 这个方法主要就是根据 Element 对应的类型进行调用 Intent 不同的方法进行取值。

private String buildStatement(String originalValue, String statement, int type, boolean isActivity) {
    switch (TypeKind.values()[type]) {
        case BOOLEAN:
            statement += (isActivity ? ("getBooleanExtra($S, " + originalValue + ")") : ("getBoolean($S)"));
            break;
        case BYTE:
            statement += (isActivity ? ("getByteExtra($S, " + originalValue + ")") : ("getByte($S)"));
            break;
        case SHORT:
            statement += (isActivity ? ("getShortExtra($S, " + originalValue + ")") : ("getShort($S)"));
            break;
        case INT:
            statement += (isActivity ? ("getIntExtra($S, " + originalValue + ")") : ("getInt($S)"));
            break;
        case LONG:
            statement += (isActivity ? ("getLongExtra($S, " + originalValue + ")") : ("getLong($S)"));
            break;
        case CHAR:
            statement += (isActivity ? ("getCharExtra($S, " + originalValue + ")") : ("getChar($S)"));
            break;
        case FLOAT:
            statement += (isActivity ? ("getFloatExtra($S, " + originalValue + ")") : ("getFloat($S)"));
            break;
        case DOUBLE:
            statement += (isActivity ? ("getDoubleExtra($S, " + originalValue + ")") : ("getDouble($S)"));
            break;
        case STRING:
            statement += (isActivity ? ("getExtras() == null ? " + originalValue + " : substitute.getIntent().getExtras().getString($S, " + originalValue + ")") : ("getString($S)"));
            break;
        case SERIALIZABLE:
            statement += (isActivity ? ("getSerializableExtra($S)") : ("getSerializable($S)"));
            break;
        case PARCELABLE:
            statement += (isActivity ? ("getParcelableExtra($S)") : ("getParcelable($S)"));
            break;
        case OBJECT:
            statement = "serializationService.parseObject(substitute." + (isActivity ? "getIntent()." : "getArguments().") + (isActivity ? "getStringExtra($S)" : "getString($S)") + ", new " + TYPE_WRAPPER + "<$T>(){}.getType())";
            break;
    }

    return statement;
}

还有最重要的一步。

// 给生成类添加方法
helper.addMethod(injectMethodBuilder.build());
// 通过Filer在编译期生成文件
JavaFile.builder(packageName, helper.build()).build().writeTo(mFiler);

至此,AutowiredProcessor.java 的源码就分析完了。

图解

经过上面的分析,大致上可以画出 @Autowired 的基本原理。

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