共计 3306 个字符,预计需要花费 9 分钟才能阅读完成。
Google Guice 容器外部有什么
前言
Maven 系列,好几天没写了,次要是这几天被 Google Guice 卡住了,原本是能够轻易带过 Guice,讲讲 guice 的用法就够了(Maven 容器的下半场:Guice,据说仅次于 Spring),然而,想着 guice 作为 maven 的底层 IOC 容器,对 guice 的了解深刻一些,对后续的 Maven 源码学习也会比拟有帮忙,因而,就在那开始剖析 guice 的源码。
guice 作为一个仅次于 Spring 的 IOC 容器,代码也不是那么好懂的,毕竟也迭代了十来年了;代码量不少,另外,我感觉代码也有点绕,就看得真心有点打瞌睡。
因为上班回来也 9 点多了,学习的工夫也不多,因而,花了好几天工夫来单步 debug,有一点点头绪,因而,这里先分享给大家,等后续了解深刻了再补充。
针对 Guice 的源码分析法
一般来说,我 debug 源码,都是从头开始,单步 debug 过来,很多时候,这种 IOC 框架啥的,启动非常复杂,一个小时也跟不完一趟;过程简短,一篇几千字的文章根本都讲不完,读者也记不住那么多货色,博主也很难讲清那么多货色。
我明天也想着换个思路吧,IOC 容器,不是分两个阶段吗,启动时,个别是筹备 IOC 容器;而运行时,就是去容器拿货色。依据我的发现,个别为了保障运行时足够快,都会事后把数据筹备好,比方,针对 singleton 类型的实例,都会事后生成(eager-initilization),寄存到容器中,就无需运行时再去生成,归根结底,就是一个空间换工夫的办法。
采纳这种空间换工夫的办法,就会有个问题,就是在数据筹备阶段(比方容器初始化阶段),要做的工作相当多,debug 过程也十分长;甚至,有时候筹备的很多数据,对于咱们的场景,基本用不上。
因而,上面我会先给大家看看,初始化胜利后的容器,是什么样的;再去简略剖析背地的启动过程。
简略 demo
一共三个类。
public interface HelloInterface {void hello();
}
public class HelloInterfaceImpl implements HelloInterface {
@Override
public void hello() {System.out.println("hello world");
}
}
再下边是启动类:
这个启动类,也就是三个局部:
- 第一个局部,就是配置:HelloInterface 这个 class,要映射到 HelloInterfaceImpl 这个实现类,后续,容器能力依据 HelloInterface 来 new 一个 HelloInterface 的实例进去。
- 初始化容器
- 运行时,从容器获取 HelloInterface 的对象
容器中有什么
假如咱们跳过初始化容器的阶段,不关怀容器如何结构,如何启动,只看:结构好的容器,是什么样的。
// 结构容器
Injector injector = Guice.createInjector(module);
在执行完下面这句后,容器就曾经初始化结束,此时,咱们打上断点,看看容器的外部:
类型
实在类型是:
// Default Injector implementation.
final class InjectorImpl implements Injector, Lookups
从它实现的接口 com.google.inject.Injector
来看,次要有以下一些外围办法:
// 获取以后容器内的全副绑定关系
Map<Key<?>, Binding<?>> getBindings();
// 依据 key,获取这个 key 对应的绑定关系。key 其实根本就是一个接口的 Class 类名
<T> Binding<T> getBinding(Key<T> key);
// 依据 class,获取这个 class 对应的绑定
<T> Binding<T> getBinding(Class<T> type);
// 依据 key,获取对应的工厂类
<T> Provider<T> getProvider(Key<T> key);
// 依据 class,获取对应的工厂类
<T> Provider<T> getProvider(Class<T> type);
// 依据 key/class,间接获取对应的实例
<T> T getInstance(Key<T> key);
<T> T getInstance(Class<T> type);
大家看到这里,是不是感觉和 Spring 的容器很像呢?
字段
-
父容器
final InjectorImpl parent;
相似于 spring,spring 也有父子容器的概念;大体就是,以后容器找不到实例,还能够去父容器找
咱们这个 demo 里,parent 是 null
-
绑定 map
final ListMultimap<TypeLiteral<?>, Binding<?>> bindingsMultimap;
存储了一些绑定关系,包含了三个默认的绑定,如:容器 injector 自身、日志 logger、stage。
-
容器选项
final InjectorOptions options;
这边是一些配置项,比方 jitdisabled,禁止隐式依赖。禁止后,你要向容器获取 Class X 的实例,那么必须先配置 X 对应的实例化形式,不会再默认尝试调用 Class X 的结构器(如果有的话)
-
隐式绑定
final Map<Key<?>, BindingImpl<?>> jitBindings = Maps.newHashMap();
比方咱们的这个实现类,就是个隐式绑定,因为咱们没配置如何实例化 HelloInterfaceImpl。
-
结构器缓存
final ConstructorInjectorStore constructors = new ConstructorInjectorStore(this);
比方咱们实现类的结构器,就被缓存了。
-
外部状态:state
看了以上几个字段,感觉也没有很特地。其实,真正重要的字段,是上面将出场的这个。
final State state;
大家看下图,会发现 state 下有不少字段,次要就有:每个 class 对应的绑定(value 就是这个 class 的实例化形式)、还有咱们代码里配了个切面也在这里;基本上,这里才是真正的容器的各种数据的存放处
接下来,咱们再看看这个绑定关系的 map。key 就是对应的接口类,value 就是说:怎么去实例化一个这个类型的实例进去,所以呢,guice 外部,为了对立,根本把 value 这部分对立成了一个工厂。如下:
而工厂类里是什么样呢?
就是蕴含了对应的实现类的结构器了。
在真正要找容器获取这个 HelloInterface 的实例时,就能够找到 HelloInterfaceImpl 的构造函数,从而结构一个实例进去。
不同的 binding 形式,外部不同的工厂类
当咱们配置了一个如下的绑定关系时:
binder.bind(String.class).toInstance("xxx");
此时,外部又是什么样呢?
这里,咱们发现外部工厂 internalFactory 的类型,和之前也不太一样了。同时,下图能够看见,工厂外部间接存了这个 String 实例的值。
总之呢,也是保障后续间接就能在容器须要一个 String 类型实例时,找到“xxx”这个对象返回回去。
从容器中获取
容器初始化好了,怎么获取呢?即如下代码怎么执行呢?
HelloInterface instance = injector.getInstance(HelloInterface.class);
咱们略微跟了下,发现就会走到如下中央,会去查问 state 外部的显示绑定 map。
获取到 binding 后,即取出 internalFactory,而后结构 / 取出对象即可。
总结
不晓得大家清晰一点了没,心愿对大家有帮忙。后续会视状况,再看看是否剖析结构容器的源码。
我是逐日, 深圳腾讯打工人,成都深圳两地重复横跳的后端 java 程序猿一枚. 之前在深圳 3 年,起初去了成都 4 年,当初又来了深圳,也开始写写前端,有须要内推的能够找我。对于一线编码实战、网络、数据库、高并发等有浓厚兴趣。
也欢送加我,拉进技术群一起交换;腾讯内推也能够找我。
本文由博客一文多发平台 OpenWrite 公布!