ARouter解析五:IoC与依赖注入

2023-11-08

终于来到了ARouter解析的第五篇了,前面陆陆续续分享了四篇ARouter框架的使用和源码内容:

ARouter解析一:基本使用及页面注册源码解析
ARouter解析二:页面跳转源码分析
ARouter解析三:URL跳转本地页面源码分析
ARouter解析四:发现服务和Fragment

这次分享下IoC思想和ARouter的自动注入这块内容。IoC是控制反转的意思,这个在后端开发中用的会比较多,也是Spring框架的核心。 控制反转一般分为两种类型,依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。在Android开发中一般自己实现倒比较少,但并不是说不常用,很多大名鼎鼎的框架都用的这种思想,比如ButterKnife,Dagger等。我们今天先分享下IoC主要的两种实现方式,再分析下ARouter在这方面的相关实践。

这次分享会按照下面的步骤进行:

1.常用的注入方式

2.依赖注入和依赖查找

3.ARouter注入源码分析

IoC这块涉及到的内容会比较多,比如注解,反射,动态代理,我们尽量用简单的语言描述,我之前的博客也有分享到这些内容,不明白的小伙伴可自行参考。

1.常用的注入方式

在开发过程中,肯定会涉及到类之间的依赖,一般都是直接new出来,比如:

class Hello{
    public Hello(){

    }
    
    public void sayHello(){
        
    }
}

public class Human {
    public static void main(String[]args) {
        Hello hello = new Hello();
        hello.sayHello();
    }
}

这里的hello实例就是用户自己new出来的,这时控制权还是在用户手中,这有几个缺点:互相依赖, 用户需要管理hello的声明周期。

所以,更进一步的注入方式有构造函数或者set方法,比如下面代码。

public class Human {
    private Hello hello;

    public Human(Hello hello) {
        this.hello = hello;
    }

    public void setHello(Hello hello) {
        this.hello = hello;
    }
}

这会就比上面代码更灵活点,也减少了依赖,但是还是需要用户去调用方法设置实例。有木有更好的办法呢?可能你也想到了注解的办法,没错,我们接着往下看。

2.依赖注入和依赖查找

依赖注入和依赖查找有什么区别呢?我们平时用的依赖注入会比较多一点,依赖注入在Android上大部分是通过自定义注解来实现,其实严格说起来依赖注入也是通过依赖查找来实现的,依赖注入用起来会比依赖查找方便。我们先来看一个简单的栗子,再看看ARouter的实践。
先来看下依赖查找,其实我们上一期分享的ARouter解析四:发现服务和Fragment就是通过依赖查找来发现服务和Fragment的。看下面的栗子,helloService是通过ARouter框架来创建的,比上面直接new构造会方便很多,但是还是用户来找到这个类的实例。

interface HelloService{
    void sayHello();
}

@Route(path = "/service/hello")
class HelloServiceImpl implements HelloService {
    Context mContext;

    @Override
    public void sayHello() {
        Toast.makeText(mContext, "Hello ", Toast.LENGTH_SHORT).show();
    }
}

public class Human {
    public static void main(String[]args) {
        HelloService helloService = ARouter.getInstance().navigation(HelloService.class);
        helloService.sayHello();
    }
}

那么上面的栗子用依赖注入该怎么实现?是不是更为方便了,不需要用户再去给出实例的路径,通过注解@Autowired就可以得到我们需要的实例。

public class Human {
    @Autowired
    private HelloService helloService;

    public static void main(String[]args) {
        ARouter.getInstance().inject(this);
        helloService.sayHello();
    }
}

上面的栗子是为了讲解清楚简单拼凑的,我们来看下ARouter官方Demo的栗子。点击依赖注入,可以给Test1Activity传递对象参数

依赖注入1.png

依赖注入2.png

我们看下Test1Activity的代码,可以看出来没有new,没有查找,只有一个注解@Autowired,和一行关键代码ARouter.getInstance().inject(this);

@Route(path = "/test/activity1")
public class Test1Activity extends AppCompatActivity {

    @Autowired
    String name;

    @Autowired
    int age;

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

    @Autowired
    TestParcelable pac;

    @Autowired
    TestObj obj;

    private long high;

    @Autowired
    String url;

    @Autowired
    HelloService helloService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test1);

        ARouter.getInstance().inject(this);

        String params = String.format(
                "name=%s,\n age=%s,\n girl=%s,\n high=%s,\n url=%s,\n pac=%s,\n obj=%s",
                name,
                age,
                girl,
                high,
                url,
                pac,
                obj
        );
        helloService.sayHello("Hello moto.");

        ((TextView)findViewById(R.id.test)).setText("I am " + Test1Activity.class.getName());
        ((TextView)findViewById(R.id.test2)).setText(params);
    }
}

那么源码是怎么做到的呢?我们接着往下看。

3.ARouter注入源码分析

上面其实就是一行关键代码ARouter.getInstance().inject(this);,所以我们跟进去看看,来到ARouter的代理类_ARouter中,首先通过以来查找获取AutowiredService的具体实现类,然后得到实例,这个不清楚的可以看下上篇分享,ARouter解析四:发现服务和Fragment。这里就不再解释了。

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

我们接着看下AutowiredService,这里会先在混存中查找是否有Test1Activity的辅助注入类,这里刚开始肯定是没有的,所以需要去加载。辅助注入类就是Test1Activity$$ARouter$$Autowired,不用说,这个就是编译期间生成的。

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

通过反射得到辅助注入类的实例后就调用inject方法注入实例。我们来看下这个类的代码。到这里就水落石出了,在跳转时将需要传递的参数写入postcard的bundle中,然后成功跳转到目标页面后就可以通过getIntent取出参数,然后分别给目标页面的每个需要注入的成员变量赋值。

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 != 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);
    }
}

我们再总结下整个注入的过程:

依赖注入.png

4.总结

今天我们分享了IoC的设计思想,也通过栗子说明了依赖注入和依赖查找,从上面的分析可以看出ARouter的依赖注入是在运行时生成辅助类,在运行时通过反射实例化辅助类并完成跳转后参数的自动注入。这里面涉及到的技术还是比较多的,比如反射,注解,APT技术,IoC,依赖注入和依赖查找,代理,算是比较综合的应用了,这些技术的我在之前的博客中都有分享过,小伙伴们可自行参考。

惯例,感谢@右倾倾的支持与理解!

后面会分享ARouter的拦截器相关的内容,感兴趣的小伙伴欢迎关注。希望我的分享能对大家有点帮助,谢谢!



作者:juexingzhe
链接:https://www.jianshu.com/p/31a1c2c3ee72
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

转载于:https://my.oschina.net/JiangTun/blog/1612721

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ARouter解析五:IoC与依赖注入 的相关文章

  • 如何在Netbeans中插入main方法(快捷方式)

    有时您想运行单个文件来快速测试某些代码 正在输入public static void main String args 每次都很乏味 怎样才能做得更快呢 由于 Netbeans 中预定义的代码模板 这很简单 只需输入psvm并按 Tab 键
  • Hashmap并发问题

    我有一个哈希图 出于速度原因 我希望不需要锁定 假设我不介意过时的数据 同时更新它和访问它会导致任何问题吗 我的访问是获取 而不是迭代 删除是更新的一部分 是的 这会导致重大问题 一个例子是向散列映射添加值时可能发生的情况 这可能会导致表重
  • 将 Hibernate 对象序列化为 JSON 时抛出异常

    好吧 我正在使用 Hibernate 将一个小型数据库加载到一些表示表的类并与数据库交互 一切都很好 我真的可以看到所有结果 而且我没有任何空字段 所有这些都已被使用 这里我展示了 主 类 表 import javax persistenc
  • 手动编辑 Jar 以更改包名称

    我有一个来自外部源的 jar 文件 jar 中的所有类都位于 com xyz 包中 我想将所有类移动到 com xyzold 包中 这是否像解压缩 jar 将 xzy 文件夹重命名为 xyzold 并重新压缩它一样简单 或者我还需要修改每个
  • jvm 次要版本与编译器次要版本

    当运行使用具有相同主要版本但次要版本高于 JVM 的 JDK 编译的类时 JVM 会抛出异常吗 JDK 版本并不重要 类文件格式版本 http blogs oracle com darcy entry source target class
  • 防止 Spring Boot 注册 Spring Security 过滤器之一

    我想禁用安全链中的 Spring Security 过滤器之一 我已经看到了防止 Spring Boot 注册 servlet 过滤器 https stackoverflow com questions 28421966 prevent s
  • 使用 Java 在 WebDriver 中按 Ctrl+F5 刷新浏览器

    我已经使用 java 刷新了 WebDriver 中的浏览器 代码如下 driver navigate refresh 如何使用 Java 在 WebDriver 中按 Ctrl F5 来做到这一点 我认为您可以使用 WebDriver 和
  • URL.setURLStreamHandlerFactory

    我正在使用带有嵌入式 Jetty 的可执行 jar 开发一个 Web 应用程序 我的jar包含一个依赖jar jar in jar 我参考了JarRsrcLoader and RsrcURLStreamHandlerFactory由 Ecl
  • 如何开始使用 Chainsaw for Log4j?

    我想开始使用 Chainsaw v2 几乎没有关于它的信息 我只找到了this http www velocityreviews com forums t140105 help using chainsaw for log4j html 但
  • Junit maven构建错误(maven-surefire-plugin:2.19.1:测试失败:分叉进程中出现错误)[重复]

    这个问题在这里已经有答案了 我通过引用创建了一个示例 struts 2 项目和 J unit 测试用例link http self learning java tutorial blogspot com au 2015 04 struts2
  • 如何比较 Struts 2 中 url 请求参数中的单个字符

    我正在读取具有单个字符的 url 参数 它将是Y or N 我必须写一个条件来检查它是否Y or N并做相应的事情 这是我写的 但似乎不起作用 总是转到其他地方 网址是
  • 将过滤器添加到 Eclipse 中的 Project Explorer

    我想向 Project Explorer 添加一个新的过滤器 以向用户隐藏一些在 Eclipse RCP 应用程序中自动创建的项目 到目前为止我已经找到了两个扩展点 org eclipse ui ide resourceFilters 允许
  • RMI 中的引用传递问题? [复制]

    这个问题在这里已经有答案了 有人可以告诉我我错在哪里 为什么这个 RMI 聊天应用程序不起作用 目标是通过远程对象或序列化对象实现客户端 服务器和逻辑之间的解耦 import javax swing import java awt even
  • JFace ColumnWeigthData 导致父级增长

    我有一个 Eclipse RCP 应用程序 并且想要在TableViewer using ColumnWeigthData as ColumnLayoutData 问题是父表单 ScrolledForm在示例代码中 每当我布局表格时都会增加
  • 容器中的 JVM 计算处理器错误?

    最近我又做了一些研究 偶然发现了这一点 在向 OpenJDK 团队抱怨之前 我想看看是否有其他人观察到这一点 或者不同意我的结论 因此 众所周知 JVM 长期以来忽略了应用于 cgroup 的内存限制 众所周知 现在从 Java 8 更新某
  • 我想在java中使用XQuery进行Xml处理

    我想用XQuery用于从 java 中的 Xml 获取数据 但我没有得到需要为此添加哪个 Jar 我在谷歌上搜索了很多 但没有得到任何有用的例子 例如我得到以下链接 https docs oracle com database 121 AD
  • 使用 JAD 反编译 java - 限制

    我正在尝试使用 Java 中的 JAD 反编译几个 jar 文件 我也尝试过 JD GUI 但运气更差 但出现了很多错误 一种类型 易于修复 似乎是内部类 但我也发现了这段代码 static int SWITCH TABLE atp com
  • 对于当前月份和日期但年份不同的日期,经过的月份计算未给出正确的结果

    我正在尝试计算自特定日期以来经过的月份 该函数工作正常 尽管如果我将今天的日期与过去的不同年份放在一起 它会给我一个月的差异 不到一个月 假设对于所有日期 该函数都运行良好 除了 如果今天是 2014 03 06 YYYY MM DD 并且
  • 无法使用 wget 在 CentOS 机器上安装 oracle jdk

    我想在CentOS上安装oracle java jdk 8 我无法安装 java jdk 因为当我尝试使用命令安装 java jdk 时 root ADARSH PROD1 wget no cookies no check certific
  • Java、Spring、Hibernate找不到org.springframework.orm.hibernate3.LocalSessionFactoryBean

    我正在尝试制作 spring hibernate ant 项目 目前我收到此错误 HTTP Status 500 type Exception report message description The server encountere

随机推荐

  • 力扣博文链接

    目录 树 堆 模拟 枚举 组合 链表 递归 宽搜 指针 进制 图论 分析 贪心 数学 构造 概率 排序 日期 KMP RMQ Trie树 对顶堆 扫描线 自动机 格雷码 字符串 思维题 逆序对 回文串 全排列 离散化 线段树 平衡树 单调栈
  • windows里的vscode 通过ssh远程到Linux服务器,显示matplotlib图形

    本地vscode安装插件 PYQT Integration 右键 py 选择 Run Current File in interactive Window 一些使用PyQt的步骤 conda activate py38 在自己的conda环
  • 用虚拟机安装linux程序

    其实这是一个很简单的工作 大牛不要嘲笑 这里只是写一个简单的流程 首先 是使用的软件 虚拟机程序Oracle VM VirtualBox http www oracle com technetwork server storage virt
  • IP地址的分类及子网掩码的计算

    目录 一 什么是IP地址 IP地址的作用及其种类 二 分类的IP地址 三 无分类编址 四 网络号 主机号和子网掩码的计算 一 1 IP地址 整个互联网中 分配给每一个主机在全世界范围内唯一的32位二进制码 2 IP地址的表示方法 为了可读性
  • Mybatis-Plus的详细使用

    一 MyBatisPlus概述 需要的基础 MyBatis Spring SpringMVC学完 为什么要学习呢 它可以节省我们大量的工作时间 所有的JDBC都可以自动化完成 JPA tk mapper MyBatisPlus 简介 是什么
  • 线上流量特训营:暴力引流10W+中小卖家流量破局技巧等

    新标题 突破流量瓶颈 线上流量特训营助力中小卖家引爆10W 流量的破局技巧 文章 引言 100字 线上流量特训营是一项旨在帮助中小卖家突破流量瓶颈的破局技巧 通过学习特训营提供的精选流量引爆策略 中小卖家可以迅速吸引超过10W的流量 实现业
  • L1-5 试试手气 (15 分)

    我们知道一个骰子有 6 个面 分别刻了 1 到 6 个点 下面给你 6 个骰子的初始状态 即它们朝上一面的点数 让你一把抓起摇出另一套结果 假设你摇骰子的手段特别精妙 每次摇出的结果都满足以下两个条件 1 每个骰子摇出的点数都跟它之前任何一
  • FWT 详解 知识点

    前言 扯淡 可以跳过 其实去年清华集训之后就想写这篇文章了 但是写了一会发现有点说不明白话 于是受限于语文水平一直没有写 前几天给人当面讲了一遍 感觉大概可以BB明白些了 picks的博客里就写着fwt怎么做 然而他并没有写为什么这样是对的
  • 【微服务架构】面向故障设计微服务架构

    微服务架构可以通过定义明确的服务边界隔离故障 但就像在每个分布式系统中一样 网络 硬件或应用程序级别问题的可能性更高 由于服务依赖关系 任何组件都可能对其消费者暂时不可用 为了最大限度地减少部分中断的影响 我们需要构建可以优雅地响应某些类型
  • 爬取上交所和深交所的年报问询函到Excel

    注意事项 需要安装一些包 如pdfminer pdfminer3k pdfplumber等 pdfminer不能解析上交所问询函 使用解析功能更为强大的pdfplumber可以解析 但是内容上可能会出现个别字重复的现象 pdfminer3k
  • 区间预测

    区间预测 MATLAB实现基于QRCNN BiGRU Multihead Attention多头注意力卷积双向门控循环单元多变量时间序列区间预测 目录 区间预测 MATLAB实现基于QRCNN BiGRU Multihead Attenti
  • Spring Boot 学习笔记整理

    spring boot 笔记 1 配置文件 1 application properties 2 application yml 作用 修改spring boot的默认设置 YAML 比XML和JSON更适合做配置文件 以数据为中心 2 Y
  • 解决鼠标右键没有文本

    解决鼠标右键没有文本文档 打开注册表 win r 输入 regedit 2 找到 txt 将默认值改为 txtfile 查看shellNew项是否存在 不存在新建 存在则改变 这个字符串值为空 F5刷新一下 或者
  • OLE接口详解

    所有 OLE Api 和接口的目的 本页 摘要 详细信息 常规 初始化和内存管理 远程处理 自定义服务 服务注册 DLL 服务器管理 杂项 COM 函数 命名 名字对象 结构化的存储 永久对象 每个事件的通知 统一数据传输 可查看对象 标准
  • HarmonyOS 自定义页面请求与前端页面调试

    一 自定义页面请求响应 Web 组件支持在应用拦截到页面请求后自定义响应请求能力 开发者通过onInterceptRequest 接口来实现自定义资源请求响应 自定义请求能力可以用于开发者自定义 Web 页面响应 自定义文件资源响应等场景
  • 每日一题:走路

    走路 题目 Daimayuan Online Judge f i j 表示第i步能否走到第j阶 include
  • uniapp打包微信小程序主包过大问题

    问题 在用uniapp打包微信小程序时提示文件超过了2M不让上传 主包中的vendor js太大1 7M有的甚至更大 解决 在HbuildX中运行时勾选上运行压缩 在微信开发者工具中上传时勾选上上传压缩 在manifest json中检查分
  • C语言 .c文件 到 .exe文件过程

    预处理 预处理相当于根据预处理命令组装成新的 C 程序 不过常以 i 为扩展名 编 译 将得到的 i 文件翻译成汇编代码 s 文件 汇 编 将汇编文件翻译成机器指令 并打包成可重定位目标程序的 o 文件 该文件是二进制文件 字节编码是机器指
  • OculusRiftS与Unity.UI的交互(1)-总览

    使用OculusIntegration包 VRTK还没有测试过 OculusIntegration提供的场景 包含了 键盘交互 VR摄像机 画布 凝视位置 光标 等节点 总览 这是默认的OVR UI场景的节点设置 之后 根据自身场景的需要
  • ARouter解析五:IoC与依赖注入

    终于来到了ARouter解析的第五篇了 前面陆陆续续分享了四篇ARouter框架的使用和源码内容 ARouter解析一 基本使用及页面注册源码解析ARouter解析二 页面跳转源码分析ARouter解析三 URL跳转本地页面源码分析ARou