Google Guice容器内部有什么

前言

Maven系列,好几天没写了,主要是这几天被Google Guice卡住了,本来是可以随便带过Guice,讲讲guice的用法就够了(这个已经讲了,在前面的文章),但是,想着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,然后构造/取出对象即可。

总结

不知道大家清晰一点了没,希望对大家有帮助。后续会视情况,再看看是否分析构造容器的源码。

【曹工杂谈】Maven IOC 容器-- Guice内部有什么的更多相关文章

  1. 【曹工杂谈】Maven源码调试工程搭建

    Maven源码调试工程搭建 思路 我们前面的文章<[曹工杂谈]Maven和Tomcat能有啥联系呢,都穿打补丁的衣服吗>分析了Maven大体的执行阶段,主要包括三个阶段: 启动类阶段,负责 ...

  2. 【曹工杂谈】Mysql-Connector-Java时区问题的一点理解--写入数据库的时间总是晚13小时问题

    背景 去年写了一篇"[曹工杂谈]Mysql客户端上,时间为啥和本地差了整整13个小时,就离谱",结果最近还真就用上了. 不是我用上,是组内一位同事,他也是这样:有个服务往数据库in ...

  3. 【曹工杂谈】Maven IOC容器的下半场:Google Guice

    Maven容器的下半场:Guice 前言 在前面的文章里,Maven底层容器Plexus Container的前世今生,一代芳华终落幕,我们提到,在Plexus Container退任后,取而代之的底 ...

  4. 【曹工杂谈】Maven底层容器Plexus Container的前世今生,一代芳华终落幕

    Maven底层容器Plexus Container的前世今生,一代芳华终落幕 前言 说实话,我非常地纠结,大家平时只是用Maven,对于内部的实现其实也不关心,我现在非要拉着大家给大家讲.这就有个问题 ...

  5. 曹工杂谈--使用mybatis的同学,进来看看怎么在日志打印完整sql吧,在数据库可执行那种

    前言 今天新年第一天,给大家拜个年,祝大家新的一年里,技术突突突,头发长长长! 咱们搞技术的,比较直接,那就开始吧.我给大家看看我demo工程的效果(代码下边会给大家的): 技术栈是mybatis/m ...

  6. 【曹工杂谈】详解Maven插件调试方法

    前言 今年的更新频率简直是降至冰点了,一方面平时加班相对多一些了,下班只想玩手机:另一方面,好像进了大厂后,学习动力也很低了,总之就,很懒散,博客的话,今年都才只更新了不到5篇. 现在慢慢有一点状态, ...

  7. 曹工杂谈:Spring boot应用,自己动手用Netty替换底层Tomcat容器

    前言 问:标题说的什么意思? 答:简单说,一个spring boot应用(我这里,版本升到2.1.7.Release了,没什么问题),默认使用了tomcat作为底层容器来接收和处理连接. 我这里,在依 ...

  8. 【曹工杂谈】Maven和Tomcat能有啥联系呢,都穿打补丁的衣服吗

    Maven和Tomcat能有啥联系呢,都穿打补丁的衣服吗 前奏 我们上篇文章,跟大家说了下,怎么调试maven插件的代码,注意,是插件的代码.插件,是要让主框架来执行的,主框架是谁呢,就是maven ...

  9. 【曹工杂谈】说说Maven框架和插件的契约

    说说Maven框架和插件的契约 前言 Maven框架就像现在公司内的各种平台方,规定一些契约,然后想办法拉动业务方,一起在这个平台上去做生态共建.Maven也是这样,其实它就是一个插件执行的框架,Ma ...

随机推荐

  1. Mol Cell丨吕志民团队揭示琥珀酰化介导的肿瘤细胞氧化应激调控新机制

    蛋白质琥珀酰化修饰 (succinylation) ,作为赖氨酸酰化修饰家族的重要一员,于2011年由芝加哥大学赵英明教授团队在Nature Chemical Biology 发文被首次报道,并被评为 ...

  2. MySQL 索引使用案例

    索引使用案例 支持多种过滤条件 假设要设计一个在线约会网站,用户信息表有很多列,包括国家.地区.城市.性别.眼睛颜色,等等.网站必须支持上面这些特征的各种组合来搜索用户,还必须允许根据用户的最后在线时 ...

  3. Java时间类从此变得清晰明了

    Java时间类 Java时间类分为Date 日期类和Calendar 日历类,相信很多小伙伴在初学时会对这个两个类的用法.区别以及有什么联系会感到疑惑,似乎懂了,但又不能具体说清,今天再带你来清晰的再 ...

  4. 【javaFX学习】(二) 面板手册

    移至http://blog.csdn.net/qq_37837828/article/details/78732591 更新 找了好几个资料,没找到自己想要的,自己整理下吧,方便以后用的时候挑选,边学 ...

  5. python数据统计之禅道bug统计

    背景 通过定期输出 每条产品的 BUG 情况,以此来反馈开发解决问题.测试跟进问题的情况:钉钉群推送提醒开发及时解决 以此我这边开始着手准备编写一个小工具,最终达到目的:自动定期发送统计报告,报告维度 ...

  6. springboot项目出现Whitelabel Error Page的问题

    springboot项目出现Whitelabel Error Page的问题 大概就是这种情况,然而昨天还是没问题的,通过对比就发现,是自己手欠了 简单来说解决办法就是将springboot的启动项目 ...

  7. 原来一条select语句在MySQL是这样执行的《死磕MySQL系列 一》

    前言 看到蒋老师的第一篇文章后就收货颇丰,真是句句戳中痛点. 令我记忆最深的就是为什么知道了一个个技术点,却还是用不好 ?不管是蒋老师所说的Redis还是本系列要展开学习的MySQL. 这是一个值得思 ...

  8. CPU 指令环 ring0,ring1,ring2,ring3

    Intel的CPU将特权级别分为4个级别:RING0,RING1,RING2,RING3. Windows只使用其中的两个级别RING0和RING3,RING0只给操作系统用,RING3谁都能用.如果 ...

  9. 记一次Orika使用不当导致的内存溢出

    hprof 文件分析 2021-08-24,订单中心的一个项目出现了 OOM 异常,使用 MemoryAnalyzer 打开 dump 出来的 hprof 文件,可以看到 91.27% 的内存被一个超 ...

  10. 题解 math

    传送门 赛时用一个奇怪的方法过掉了 首先\(b_i\)的有效范围是\([0, k-1]\) 发现不同的\(a_i*b_i\)会有很多重的 考虑把\(a_i\%k\),然后由小到大排序 按顺序扫,如果某 ...