我也谈谈guice, 简单就是美

来源:互联网 发布:软件测试基本理论知识 编辑:程序博客网 时间:2024/04/30 02:35

一直以来, 我都是spring的忠实fans, 使用它也有好几年了.
看着它从1.0发展到现在的2.5, 给我感觉就是, 长大了, 而且逐渐成为一个成熟的一站式解决方案.

但有的时候, 我只想要一个ioc容器, 它还合适吗?
于是焦点定位在了google的guice. 当然轻量级容器有很多, hivemind, picoContainer, plexus 等都不错的, 但是他们生不逢时, guice是在Java最重要的几个特性发布后, 才诞生的, 本身设计理念上要好一些.

选择有以下几点理由:

1, 它的身材很小, jar包只有500多k.功能上, 是一个纯净的ioc容器, 不会有过多依赖.
2, 08年获得了jolt award.(不是主要理由)
3, 性能大幅超越spring.(不过号称spring2.5也提性能了)
3, 开发调试比较简单, 直接new一个容器, 不需要载入配置文件就可以用了.
4, 官方文档内容比较精简, 看完应该不需要1个小时, 那么, 学习成本又降低了.
5, 开发者是google的工程师, 并且google内部大量使用该框架, 维护不成问题.
6, 基于annotation, 不需要引入配置文件的复杂性, 离代码更近. 当然, annotation存在低侵入性, 这可能是它唯一的缺点.(适用于移植性要求不高或只需对外暴露服务的项目.)
7, 支持静态注入.

如果还没看过guice的朋友, 只需要耽误你半个小时, 看看这位网友写得一篇总结, 短小精悍, 总结

架构设计

guice injector结构

从图中可以看到, Injector包含N个binding(类跟实例的绑定关系)组成, binding是在创建Injector时, 由指定的Module或guice提供的一些辅助annotation(比如implementBy)生成的.
每个binding由 key , scope, provider组成, 其中scope可以不需要, 默认是原型, 也可以自己指定singleton等.
key是binding的唯一描述, 一般包含类和annotation, 其中annotation不是必须的, 但有annotation可以使同一个类对应不同子类或接口实现.
每一个binding都对应一个provider, 用于生成实例, 维护实例的生命周期. 类似Factory.

业务代码主要是通过injector来获取依赖注入后的实例.

注入流程:
注入流程序列图

使用建议

1, guice有3种注入方式, constructor, setter, field.
其中field注入是最简单的, 它支持直接向private field进行注入, 实现上, 是通过反射改变accessible, 避免了jvm的安全检查.

如果项目不需要考虑以后移植到spring或其他ioc容器的话, 用成员变量注入会方便很多.

2, Injector里有个叫 injectMember 的方法, 用于针对指定对象触发注入.
这个, 感觉适用于在基类里操作.可以减少guice跟业务代码的耦合.

比如单元测试的容器初始化基类, web应用的controller里注入model等.

3, guice里有个设计是, 通过自定义的annotation来为同一个接口注入不同的实例.(类似spring里的bean id)
这里, 在binding的时候, annotationWith 可以使用 Names.named() 这个静态方法避免新写annotation, 来生成一个指定名字的 NamedImpl 对象, 它是 @Named 这个annotation接口的实现.

guice注入的时候, 就是通过判定你写的 @Named(“xx”) 跟你注册的 NamedImpl 对象是否equals.

4, 通常不建议使用guice里提供的额外annotation, 比如 在接口上指明ImplementedBy(xx.class),说明该接口的默认实现是xx.class, 这种方式使接口依赖于实现, 当你的xx.class实现改名后, 连编译都通不过.
还有一个在实现类上, 注释 singleton 表示单例scope的, 也跟 implementedBy 类似, 有侵入性.

还是推荐在 Module 里设置 binding, 相当于配置的作用.

5, 建议将常量配置在外部 properties 里, 然后在Mdoule里配置binding时, 通过Names.bindProperties 工具方法将properties里定义的key-value字符串注册到guice容器里.代码里只要使用 @Named(“key name”) 就可以注入了.

6, 不要忽视Stage, 即 PRODUCTION(生产环境) 和 DEVELOPMENT(开发环境)对bean的实例化规则.前者会在容器初始化时, 实例化所有的bean. 后者会在使用到该bean时, 才实例化.
所以, 生产环境没必要用lazy init, 大胆选用 PRODUCTION.

7, 如何控制类似spring的InitializingBean和init-method这种生命周期动作?
guice里可以通过编写 Provider 实现init动作, 这里推荐自己搞个annotation,比如@init-method, 然后在provider里反射判定, 调一把.

另外, 还有一种更原始的方式 就是在构造函数调init.

8, 单元测试的时候, 针对一些需要初始化很久的bean, 用bind(StartupTask.class).asEagerSingleton()先初始化.

9, 编写 Module 时, 继承 AbstractModule, 可以简化binding配置代码.

0 0
原创粉丝点击