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的话 只是打印一句话。
- 既然有了注解,那么就肯定有注解处理器啦,下面看看这个注解处理器(注解处理器这里面会自动生成文件)。我们一段一段来看
首先:
@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); }}
- ARouter 依赖注入实现原理(源码解析)
- ARouter源码解析05-自动参数注入
- Spring源码解析 依赖注入
- Spring源码解析 依赖注入
- Spring源码解析 依赖注入
- Spring源码剖析——依赖注入实现原理
- Spring源码剖析——依赖注入实现原理
- Spring源码剖析——依赖注入实现原理
- ARouter源码解析06-openDebug
- 依赖注入的实现原理
- DI 依赖注入实现原理
- 阿里路由框架--ARouter 源码解析之初始化ARouter
- spring 依赖注入xml配置原理解析
- spring 依赖注入注解配置原理解析
- 开源项目源码解析-依赖注入
- dagger2依赖注入框架源码解析
- ARouter源码解析01-编译生成文件
- ARouter源码解析02-加载路由表单
- [C#]DataTable使用过程中常用的Select、Find和Compute方法
- HDU 2089 不要62【数位DP】
- Android仿今日头条和知乎等App顶部滑动导航实现代码分析及源码下载
- java 画图 graphics
- Java进阶(六)从ConcurrentHashMap的演进看Java多线程核心技术
- ARouter 依赖注入实现原理(源码解析)
- Selenium与phantomjs安装与环境配置,以及易班网站模拟登陆操作
- 区分编译型语言,解释型语言是否还有意义?
- C++和Lua交互教程(基于LuaBridge)
- 39、C#项目开发注意
- 在同一台服务器上配置多个Tomcat的方法
- spring boot 1.5.7 搭建基础项目框架一
- 跟大师学习系统编程---操作系统加载(1)
- [容斥] Topcoder SRM div1-3 12004. SetAndSet