关于java:曹工杂谈Maven-IOC-容器Guice内部有什么

50次阅读

共计 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 公布!

正文完
 0