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

来源:互联网 发布:php常用数组函数 编辑:程序博客网 时间:2024/06/06 15:44

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<TypeElement, List<Element>> 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<? extends TypeElement> 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<? extends Element> 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<Element> 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<Element> 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<TypeElement, List<Element>> entry : parentAndChild.entrySet()) {                //生成inject方法(都是javapoet的语法)                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("."));                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);  }}
原创粉丝点击