ARouter 依赖注入实现原理(源码解析)

ARouter 依赖注入实现原理

  • 这里只是记录一下ARouter中的依赖注入的源码实现方式,对于ARouter的其他分析,比如路由等,将会在后面的博客中进行分享。

1.首先我们知道要进行依赖注入的话,得先添加一个注解比如

    @Autowired
    String name;
    @Autowired
    int age;

2.然后我们在看看这个注解的定义:

@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 "No desc.";
}

在这里 有两个属性值得我们关注 就是name和required (第三个是 description的,已经废弃了)
name属性就是给它一个别名 比如

 @Autowired(name = "boy")
 boolean girl;

在传值的时候就可以用别名来传递了
required属性表示进行null判断,如果是iProvider的子类 且 为null 则抛出异常,如果不是iProvider的子类 为null的话 只是打印一句话。

  1. 既然有了注解,那么就肯定有注解处理器啦,下面看看这个注解处理器(注解处理器这里面会自动生成文件)。我们一段一段来看

首先:

@AutoService(Processor.class)
@SupportedOptions(KEY_MODULE_NAME)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes({ANNOTATION_TYPE_AUTOWIRED})
public class AutowiredProcessor extends AbstractProcessor {
    private Filer mFiler;       // File util, write class file into disk.
    private Logger logger;
    private Types types;
    private TypeUtils typeUtils;
    private Elements elements;
    private Map> parentAndChild = new HashMap<>();   // Contain field need autowired and his super class.
    private static final ClassName ARouterClass = ClassName.get("com.alibaba.android.arouter.launcher", "ARouter");
    private static final ClassName AndroidLog = ClassName.get("android.util", "Log");

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);

        mFiler = processingEnv.getFiler();                  // Generate class.
        types = processingEnv.getTypeUtils();            // Get type utils.
        elements = processingEnv.getElementUtils();      // Get class meta.

        typeUtils = new TypeUtils(types, elements);

        logger = new Logger(processingEnv.getMessager());   // Package the log utils.

        logger.info(">>> AutowiredProcessor init. <<<");
    }
}

这一段代码中,我们最需要知道的一点就是parentAndChild,这个容器装了当前类中所有的需要依赖注入的字段(这段就比较水了,哈哈哈)。

接下来看到这段代码:

 @Override
    public boolean process(Set set, RoundEnvironment roundEnvironment) {
        if (CollectionUtils.isNotEmpty(set)) {
            try {
                logger.info(">>> Found autowired field, start... <<<");
                categories(roundEnvironment.getElementsAnnotatedWith(Autowired.class));
                generateHelper();

            } catch (Exception e) {
                logger.error(e);
            }
            return true;
        }

        return false;
    }

这段代码中会调用2个方法

categories(roundEnvironment.getElementsAnnotatedWith(Autowired.class));
generateHelper();

首先先看categories();

private void categories(Set elements) throws IllegalAccessException {
        if (CollectionUtils.isNotEmpty(elements)) {
            for (Element element : elements) {
                TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();

                if (element.getModifiers().contains(Modifier.PRIVATE)) {
                    throw new IllegalAccessException("The autowired 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 childs = new ArrayList<>();
                    childs.add(element);
                    parentAndChild.put(enclosingElement, childs);
                }
            }

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

第一步(获取下标为0的element的TypeElement):

    TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();

第二步(判断当前字段的可见性,如果是private,就抛出错误)

 if (element.getModifiers().contains(Modifier.PRIVATE)) {
                    throw new IllegalAccessException("The autowired fields CAN NOT BE 'private'!!! please check field ["
                            + element.getSimpleName() + "] in class [" + enclosingElement.getQualifiedName() + "]");
                }

第三步(先看容器里面是否有当前class的key,如果没有就添加进去,如果有,则直接把value加入其中,这一步貌似不需要讲解,不过凑凑字数):

if (parentAndChild.containsKey(enclosingElement)) { // Has categries
                    parentAndChild.get(enclosingElement).add(element);
                } else {
                    List childs = new ArrayList<>();
                    childs.add(element);
                    parentAndChild.put(enclosingElement, childs);
                }

然后看看generateHelper()[这个是生成代码文件的主要方法]

这个方法的代码有点长,还是一段一段的看
第一段:

    TypeElement type_ISyringe = elements.getTypeElement(ISYRINGE);
    TypeElement type_JsonService = elements.getTypeElement(JSON_SERVICE);
    TypeMirror iProvider = elements.getTypeElement(Consts.IPROVIDER).asType();
    TypeMirror activityTm = elements.getTypeElement(Consts.ACTIVITY).asType();
    TypeMirror fragmentTm = elements.getTypeElement(Consts.FRAGMENT).asType();
    TypeMirror fragmentTmV4 = elements.getTypeElement(Consts.FRAGMENT_V4).asType();

    // Build input param name.
    ParameterSpec objectParamSpec = ParameterSpec.builder(TypeName.OBJECT, "target").build();

这一段就没什么好说的了 TypeElement , TypeMirror 都是一些在apt中使用的类,平时不太能用到,笔者也就知道这些东西,到底怎么用呢,也不是特别清楚,只能说是需要用到的时候在查吧,其他他们都是对类的一些描述。 ParameterSpec 这个是一个第三方库(javapoet)的对象,这个库可以用来生成java文件,解放你的双手,关于这个库的使用,大家可以去百度一下,或者 传送门

第二段:

if (MapUtils.isNotEmpty(parentAndChild)) {
            for (Map.Entry> entry : parentAndChild.entrySet()) {
                //生成inject方法(都是javapoet的语法)
                MethodSpec.Builder injectMethodBuilder = MethodSpec.methodBuilder(METHOD_INJECT)
                        .addAnnotation(Override.class)
                        .addModifiers(PUBLIC)
                        .addParameter(objectParamSpec);
                //获取到当前注解的类名
                TypeElement parent = entry.getKey();
                //获取这个类中需要依赖注入的字段
                List childs = entry.getValue();

                String qualifiedName = parent.getQualifiedName().toString();
                String packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf("."));
                String fileName = parent.getSimpleName() + NAME_OF_AUTOWIRED;

                logger.info(">>> Start process " + childs.size() + " field in " + parent.getSimpleName() + " ... <<<");
                //这里是生成public class xxx implements ISyringe 
                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);;
                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();
                    //如果是iProvider的子类
                    if (types.isSubtype(element.asType(), iProvider)) {  // It's provider
                        //没有设置name,则通过byType的方式
                        if ("".equals(fieldConfig.name())) {    // User has not set service path, then use byType.

                            // Getter
                            //这段代码生成substitute.xxx = ARouter.getInstance().navigation(xxx.class);
                            injectMethodBuilder.addStatement(
                                    "substitute." + fieldName + " = $T.getInstance().navigation($T.class)",
                                    ARouterClass,
                                    ClassName.get(element.asType())
                            );
                        } else {    // use byName
                            // Getter
                            //这段代码生成了substitute.xxx= ARouter.getInstance()build("yyy").navigation();
                            injectMethodBuilder.addStatement(
                                    "substitute." + fieldName + " = ($T)$T.getInstance().build($S).navigation();",
                                    ClassName.get(element.asType()),
                                    ARouterClass,
                                    fieldConfig.name()
                            );
                        }

                        // Validater
                        //这里就是文章最前面讲到的required字段,如果为null则crash
                        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();
                        }
                    } else {    // It's normal intent value
                    //非iProvider的子类
                        String statment = "substitute." + fieldName + " = substitute.";
                        boolean isActivity = false;

                        if (types.isSubtype(parent.asType(), activityTm)) {  // Activity, then use getIntent()
                        //如果是activity的子类
                            isActivity = true;
                            statment += "getIntent().";
                        } else if (types.isSubtype(parent.asType(), fragmentTm) || types.isSubtype(parent.asType(), fragmentTmV4)) {   // Fragment, then use getArguments()
                            //如果是fragment的子类
                            statment += "getArguments().";
                        } else {
                            throw new IllegalAccessException("The field [" + fieldName + "] need autowired from intent, its parent must be activity or fragment!");
                        }
                        //buildStatement方法 稍后再讲
                        statment = buildStatement(statment, typeUtils.typeExchange(element), isActivity);
                        if (statment.startsWith("serializationService.")) {   // Not mortals
                            injectMethodBuilder.beginControlFlow("if (null != serializationService)");
                            //如果没有设置name则直接用字段名字,如果设置了name则用name的名字
                            injectMethodBuilder.addStatement(
                                    "substitute." + fieldName + " = " + statment,
                                    (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(statment, 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. <<<");
        }

再看看buildStatement()方法

private String buildStatement(String statment, int type, boolean isActivity) {
        if (type == TypeKind.BOOLEAN.ordinal()) {
            statment += (isActivity ? ("getBooleanExtra($S, false)") : ("getBoolean($S)"));
        } else if (type == TypeKind.BYTE.ordinal()) {
            statment += (isActivity ? ("getByteExtra($S, (byte) 0)") : ("getByte($S)"));
        } else if (type == TypeKind.SHORT.ordinal()) {
            statment += (isActivity ? ("getShortExtra($S, (short) 0)") : ("getShort($S)"));
        } else if (type == TypeKind.INT.ordinal()) {
            statment += (isActivity ? ("getIntExtra($S, 0)") : ("getInt($S)"));
        } else if (type == TypeKind.LONG.ordinal()) {
            statment += (isActivity ? ("getLongExtra($S, 0)") : ("getLong($S)"));
        } else if (type == TypeKind.FLOAT.ordinal()) {
            statment += (isActivity ? ("getFloatExtra($S, 0)") : ("getFloat($S)"));
        } else if (type == TypeKind.DOUBLE.ordinal()) {
            statment += (isActivity ? ("getDoubleExtra($S, 0)") : ("getDouble($S)"));
        } else if (type == TypeKind.STRING.ordinal()) {
            statment += (isActivity ? ("getStringExtra($S)") : ("getString($S)"));
        } else if (type == TypeKind.PARCELABLE.ordinal()) {
            statment += (isActivity ? ("getParcelableExtra($S)") : ("getParcelable($S)"));
        } else if (type == TypeKind.OBJECT.ordinal()) {
            statment = "serializationService.json2Object(substitute." + (isActivity ? "getIntent()." : "getArguments().") + (isActivity ? "getStringExtra($S)" : "getString($S)") + ", $T.class)";
        }

        return statment;
    }

这个方法就是根据需要注入的字段的类型,去拼接相应的代码。

以上就是自动生成代码的部分,可要怎么样才能实现注入呢,请继续看下去。

首先 我们需要在onCreate()中写一段代码

ARouter.getInstance().inject(this);

继续跟进代码

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

再次进入

static void inject(Object thiz) {
        AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
        if (null != autowiredService) {
            autowiredService.autowire(thiz);
        }
    }

发现这是_ARouter的一个静态代码块。就是一开始就会被加载进内存中,然后我们在进入autowire()方法

 @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();
                }
                autowiredHelper.inject(instance);
                classCache.put(className, autowiredHelper);
            }
        } catch (Exception ex) {
            blackList.add(className);    // This instance need not autowired.
        }
    }

看到这里 大家就明白了吧,首先通过传入的对象,拿到对象的类名,然后通过这个类名去构造一个之前apt生成的与这个类相关的Autowired对象,比如 Test1Activity 中有字段需要依赖注入,那么这里就是构造一个Test1Activity$$ARouter$$Autowired 的对象,然后调用inject() 方法,将当前对象传递进去,这样就完成了我们的依赖注入了。
我们可以看看 apt生成的类

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

  @Override
  public void inject(Object target) {
    serializationService = ARouter.getInstance().navigation(SerializationService.class);;
    Test1Activity substitute = (Test1Activity)target;
    substitute.name = substitute.getIntent().getStringExtra("name");
    substitute.age = substitute.getIntent().getIntExtra("age", 0);
    substitute.girl = substitute.getIntent().getBooleanExtra("boy", false);
    substitute.pac = substitute.getIntent().getParcelableExtra("pac");
    if (null == substitute.pac) {
      Log.e("ARouter::", "The field 'pac' is null, in class '" + Test1Activity.class.getName() + "!");
    }
    if (null != serializationService) {
      substitute.obj = serializationService.json2Object(substitute.getIntent().getStringExtra("obj"), TestObj.class);
    } else {
      Log.e("ARouter::", "You want automatic inject the field 'obj' in class 'Test1Activity' , then you should implement 'SerializationService' to support object auto inject!");
    }
    substitute.url = substitute.getIntent().getStringExtra("url");
    substitute.helloService = ARouter.getInstance().navigation(HelloService.class);
  }
}

你可能感兴趣的:(android)