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

前言

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

Guice的应用也还比较广泛,以下轮子中(仅部分)都有它活跃的身影:

  • google内部
  • scalatest
  • TestNG
  • Caffeine Cache
  • Spring Security Config
  • elastic search
  • jenkins

这很多轮子,都是直接用的Guice,那是因为没什么历史包袱;但Maven不一样,maven之前用自己的IOC轮子,有自己独特的定义组件的方式(比如Spring通过@Component注解来定义);但是Guice作为一个独立的IOC框架,那肯定是不认识Maven中的组件的。

因此,这中间肯定需要兼容啊,Maven的组件,还是用的Plexus IOC容器那套方式去定义,但是他们开发了一个中间层,把那些组件解析后,存放到了Guice容器中。

这里说,把组件解析后,存放到了Guice容器中,这个也不是特别准确,更准确的说法是,放到了基于Guice进行了一层封装的一个容器中,这个容器叫做:sisu,由eclipse在维护这个开源项目(https://www.eclipse.org/sisu/)。

可能你就疑惑了,就一个破IOC,搞得多有技术含量一样,还一层套一层。。这个我们就先不管了,这期我先讲Guice,然后大家就懂了,为啥Sisu要要封装一层了。总结一下,依赖路径是:

最底层的是google Guice --》 sisu(eclipse)--》 sisu-plexus兼容层--》plexus --》maven。

好了,开始正文。

Guice是什么

根据wiki的描述,Guice就是依赖注入框架,由google开源。主要特点就是:支持以java注解的方式配置组件及依赖。最早的版本应该是在2007年,我还找到一篇当年的报道 https://developers.googleblog.com/2007/03/

当时还是2007年,而2004年的时候,支持注解的JDK1.5才放出来,与此同时,Spring早期版本主要也还是以xml配置为主,Guice在那时就支持全注解配置,以当时的眼光来看,很前沿了。

接下来,我们就来仔细了解下Guice的用法。

核心理念

在开始讲之前,我说下我对IOC框架的理解先。很多时候,可以简单地说,IOC容器是一个map,一个放东西的地方,就像一个中药房,每个格子里会放一种药材,而每个格子上,有一个标签,来说明里面放的是什么药材。

既然是放东西的地方,核心就是两个部分,怎么放,放的时候,可能就要考虑到后续怎么找的问题。比如,如果你打算只支持根据物品的类型来找,那你要考虑到:如果这个类型的物品有多个,要怎么办?怎么区分这多个物品?

如果你打算解决这个问题,那你可能就会想:那我放的时候,再给这些物品取个名字吧,免得多个相同类型的物品,分不清。

甚至呢,你可能会考虑,物品的权限隔离,比如,物品上再加个存放人的字段,以后取得时候,就只能:自己取自己的,不能取别人的。

反正,最终还是看需求,一般来说,像我们spring这种,按类型就差不多了,一个类型多个实现的时候,再根据名字区分一下就ok。

而Guice呢,我这边会重点讲解:怎么存。至于取,可能还分成两种,依赖注入和直接从容器中取。但是依赖注入的底层实现,也是:发现我依赖的某个东西没有,就去容器里取。

所以,取东西,我们只需要关注:直接从容器中怎么获取就行;我这边就不会特别关注依赖注入的问题。

Guice中,存东西的多种方式

概览

存东西,在Guice的文档里,名词叫做Binding,中文就是绑定吧。

https://github.com/google/guice/wiki/Bindings

绑定是什么意思,就是我最终可能需要从容器中获取ClassA类型的对象。既然要取,那还得先存,存的时候,我就要建立绑定:ClassA --》该Class类型对象的获取方式。

其实还是很简单的。下边就跟着我的代码例子,我们来看看。

以下全部的代码,都在:

https://gitee.com/ckl111/maven-3.8.1-source-learn/tree/master/my-test-modules/official-guice-demo

1. linkedbinding-绑定接口到实现类

先来一个接口和实现:

public interface HelloInterface {
    void hello();
}
public class HelloInterfaceImpl implements HelloInterface {
    @Override
    public void hello() {
        System.out.println("hello world");
    }
}

再来看看,怎么放到容器,和简单的从容器中取出来的方法:

大家看我代码截图,放东西的时候,就是要指定这个接口,对应的实现类。

取东西的时候,再去根据接口取就行了。

2.BindingAnnotations 一个类型有多个实现类的时候的绑定方式

接口和多个实现类:

interface HelloInterface {
    void hello();
}
class HelloInterfaceImpl implements HelloInterface {

    @Override
    public void hello() {
        System.out.println("hello world");
    }
}

class HelloInterfaceImpl2 implements HelloInterface {

    @Override
    public void hello() {
        System.out.println("hello world");
    }
}

如果我们还是按前面的办法去取,框架就晕圈了,多个实现类,我给你哪一个呢?麻烦再明确一下吧,ok吗

Guice有个注解,叫Named,可以加在各种地方,注解本身,支持设置名称。

这里意思就是说,你接口不是多个实现吗,那我们这样:接口+注解,才算是唯一的key,这样ok了吧。

因此,现在就变成了这样:接口+注解1 --》 实现类1;接口 + 注解2 --》 实现类2.

那我怎么取呢?简单啊,怎么存,怎么取,存的时候,用的接口+注解,取的时候,也需要这样。

既然,可以用官方的Named注解,那其实自己的注解也一样可以用。

3. InstanceBindings 接口直接绑定一个单例对象

如果同一个类型,要绑定到多个实例的情况,同前面的处理方式一样。

4. 绑定到工厂方法:授人以鱼不如授人以渔

前面都是些直来直去的办法,这次不一样,我只告诉你,这个东西的获得方法。

5. 不用接口了,直接绑定一个实现类

前面都是根据一个接口类,去取接口对应的实现之类的。这次不一样,直接就是一个实现类了。

public class UtilService {
}

像上面这个情况,那肯定是直接调用这个类的构造函数了。

6. 接口绑定到一个构造函数:ToConstructorBindings

哎,我是越来越无语了,Guice的骚操作真是多啊。

7. 内置的不用绑就能用的

主要有:Logger、Injector本身(就是相当于可以帮你注入容器自身)

8. 能不能不绑定直接用

用惯了spring的我们,现在已经是不需要这么绑来绑去了。我们看看Guice的支持怎么样

不绑定的话,可以这样:

@ImplementedBy(TestInterfaceImpl.class)
interface TestInterface {
}

这就相当于,你要自己指定一下,你的实现类是谁,或者你的provider是谁。但是官方不建议用这种隐式绑定,不知道为啥,还出了个选项,专门禁用隐式绑定。

9. 一个接口多个实现类,一次性全获取回来

这个场景,就是一次性把多实现类一把取回来,放到一个集合里给你。

这个场景我没写代码,大家自己看一下文档,也简单。

10. 注入的方式

前面说了很多怎么手动从容器里面取,当然了,要自动注入的话,也是支持:构造器注入、field注入等等方式。

如以下为构造器注入:

其他支持的特性

其他的,比如循环依赖、aop也是大体支持的,只是这个容器在安卓端用,会有问题,因为aop好像不太支持,所以给安卓端还专供了一个去掉aop的版本。

循环依赖之类的,具体实现还没怎么看过。

另外,guice默认生成的是多例(类比spring的prototype,而不是singleton),但是本身也是支持singleton的,我前面的代码例子有。

最大槽点

可以看出,Guice是很轻量,轻量的意思是,功能没Spring那么全,所以,我们还需要去显式地:配置每个接口,要怎么获取它的对象(方法也是五花八门,哈哈哈,如前面展示的)。

当然,配置ok后,我们获取某对象的时候,它会帮我们去完成自动注入的东西。

但是,它也不支持类路径扫描啊,比如给个包名,自动去扫描这个包下面的组件,反正官方不支持,说是有第三方插件,还没试过。

总结

在各种轮子里,用来管理自己的代码间的相互依赖,用Guice确实足够了,用在业务代码,就还是有点累。

因为,主要是:各种依赖要自己配,只是集中在一个地方配置而已,没有像spring那样,约定通过接口找对象时,默认就是找实现类,然后反射生成对象。

这一点来说,确实是:约定优于配置,就像Maven为啥打败了ant,也是这个道理。

另外的问题就是,不支持spring的那种component-scan。

基于这两个问题呢,方法肯定是有的,所以,Maven也足够聪明,没有直接基于Guice,而是选择了基于Guice封装后的Sisu,而Sisu就可以解决我们说的问题,支持类路径扫描之类的。

我们看看sisu怎么介绍自己:

就是比Guice多了些看起来还很不错的、Spring早已有了的特性吧。回头我们肯定要再介绍sisu的。

再见,以上。

本文由博客一文多发平台 OpenWrite 发布!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,898评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,401评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,058评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,539评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,382评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,319评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,706评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,370评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,664评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,715评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,476评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,326评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,730评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,003评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,275评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,683评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,877评论 2 335

推荐阅读更多精彩内容