当前位置: 首页 > 后端技术 > Java

【曹工杂谈】MavenIOC容器--Guice里面有什么

时间:2023-04-01 17:13:25 Java

GoogleGuice容器里面有什么随便写的自己带了Guice,说说guice的用法就够了(Maven容器后半部分:Guice,听说仅次于Spring),但是,想着guice作为底层IOCmaven的容器,让我对guice有了更深的理解,对guice有了更深的理解。Maven源码学习也会更有帮助,那就开始分析那里的guice源码吧。作为仅次于Spring的IOC容器,guice的代码可不是那么容易看懂的。毕竟迭代了十几年;因为下班回来已经9点多了,没有太多时间研究,所以花了几天的时间在单步调试上,算是有了一点头绪。所以这里先分享给大家,等后续了解深入后再补充。对于Guice的源码分析方法,一般来说,我在调试源码的时候,都是从头开始,一步调试。很多时候,这个IOC框架的启动是很复杂的,一个小时都做不完;过程很长,一篇几千字的文章很难写完,读者记不住那么多东西,博主也解释不清那么多东西。我今天也在考虑改变我的想法。IOC容器不是分两个stage的吗?启动时,一般准备好IOC容器;跑的时候,就是去容器里拿东西。根据我的发现,为了保证运行时间足够快,会提前准备好数据。例如,对于单例类型的实例,它会被预先生成(eager-initilization)并存储在容器中,因此不需要在运行时生成它。归根结底,它是一种以空间换取时间的方法。这种用空间换时间的方式,会出现一个问题,就是在数据准备阶段(比如容器初始化阶段),需要做的工作很多,调试过程也很长;甚至,有时会准备很多数据。在我们的场景中,我们根本不需要它。因此,下面我先给大家看看初始化成功后的容器是什么样子的;那么我就简单分析下它背后的启动过程。简单演示中包含三个类。publicinterfaceHelloInterface{voidhello();}publicclassHelloInterfaceImplimplementsHelloInterface{@Overridepublicvoidhello(){System.out.println("helloworld");}}下面是启动类:这个启动类,也就是三部分:第一部分是配置:类HelloInterface必须映射到实现类HelloInterfaceImpl,然后容器就可以基于HelloInterface创建一个新的HelloInterface实例.初始化容器运行时,容器中有什么从容器中获取HelloInterface对象假设我们跳过初始化容器的阶段,不关心容器是如何构造或启动的,只看:构造了什么容器看起来像。//构造容器Injectorinjector=Guice.createInjector(module);执行完上面这句话后,容器就已经初始化好了。此时,我们打个断点,往容器内部看:类型的真实类型是://DefaultInjectorimplementation.finalclassInjectorImplimplementsInjector,Lookups从它实现的接口com.google.inject.Injector,它主要有以下核心方法://获取当前容器中的所有绑定关系Map,Binding>getBindings();//根据key获取key对应的绑定关系。key基本就是一个interface的类名BindinggetBinding(Keykey);//根据class,获取这个class对应的bindingBindinggetBinding(Classtype);//根据key,获取对应的工厂类ProvidergetProvider(Keykey);//根据class,获取对应的工厂类ProvidergetProvider(Classtype);//根据key/class,直接获取对应的instanceTgetInstance(Keykey);TgetInstance(Classtype);大家看到这里,是不是觉得很像Spring的容器呢?该字段的父容器为finalInjectorImplparent;与spring类似,spring也有父子容器的概念;一般来说,如果当前容器找不到实例,也可以去父容器中找我们这个demo中,parentisnullbindingmapfinalListMultimap,绑定>bindingsMultimap;存储了一些绑定关系,包括三个默认的绑定,比如:containerinjector本身,loglogger,stage。容器选项最终InjectorOptions选项;这里有一些配置项,比如jitdisabled,就是禁止隐式依赖。禁止后,如果要从容器中获取ClassX的实例,必须先配置X对应的实例化方法,默认不会尝试调用ClassX的构造函数(如果有的话)隐式绑定finalMap,BindingImpl>jitBindings=Maps.newHashMap();比如我们的实现类是隐式绑定,因为我们没有配置如何实例化HelloInterfaceImpl。构造函数缓存finalConstructorInjectorStoreconstructors=newConstructorInjectorStore(this);例如,如果我们实现一个类的构造函数,它就会被缓存。内部状态:state看了上面的字段,感觉也不是很特别。事实上,真正重要的领域是将在下面出现的领域。最终状态;如果你看下图,你会发现state下面有很多字段,主要包括:每个类对应的binding(value是这个类的实例化方法),在我们的代码中也有一段Here;基本上,这是存储真实容器的各种数据的地方。接下来我们看一下绑定关系的图。key是对应的接口类,value是:如何实例化这个类型的实例。所以在Guice内部,为了统一起见,value部分基本都统一到了一个factory里面。如下:而在工厂类中又是怎样的呢?它是包含相应实现类的构造函数。真正找容器获取HelloInterface的实例时,可以找到HelloInterfaceImpl的构造函数来构造实例。不同的绑定方式,不同的内部工厂类当我们配置一个绑定关系时,如下:binder.bind(String.class).toInstance("xxx");此时,内部是怎样的?在这里,我们发现内部工厂internalFactory的类型和之前不一样了。同时,如下图可以看出,String实例的值是直接存储在工厂内部的。总之,也保证了当容器需要String类型的实例时,能找到对象“xxx”直接返回。从容器中获取容器初始化好了,如何获取呢?即如何执行下面的代码?HelloInterfaceinstance=injector.getInstance(HelloInterface.class);稍微跟进一下,发现会去到下面这个地方,查询状态里面的显示绑定图。获取绑定后取出internalFactory,然后构造/取出对象。不知道大家有没有总结的比较清楚,希望对大家有所帮助。后面看情况看要不要分析容器的源码。我是朱日,深圳腾讯员工,反复跳槽成都和深圳的后端java程序员。我在深圳3年,然后去了成都4年。需要推荐的可以找我。对一线编码实践、网络、数据库、高并发等有浓厚兴趣,也欢迎大家加我,拉进技术群一起交流;也可以在腾讯内推找到我。本文由博客多发平台OpenWrite发布!