美文网首页
ARouter 之@Autowired源码分析

ARouter 之@Autowired源码分析

作者: 你怕是很皮哦 | 来源:发表于2020-07-21 17:53 被阅读0次

    @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

    相关文章

      网友评论

          本文标题:ARouter 之@Autowired源码分析

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