- 浏览: 193565 次
- 性别:
- 来自: 北京
最新评论
-
bzhao:
开启了一个很牛逼的话题!
提高程序员的准入门槛? -
迷人阳光love:
不知两年多了,lz整理的从问题出发的模式是否还在??很是渴望得 ...
学习模式,不如先了解问题 -
迷人阳光love:
lz说到了我的心坎里,这是您10年发的文章,现在 也没找到一个 ...
学习模式,不如先了解问题 -
toafu:
我的理解是,持续集成和交付也解决不了人的问题。
为什么我的敏捷项目有如此多的问题? -
liaofeng_xiao:
《不可承受的生命之轻》 ,真没看出太多内容~
近期看的书和电影
Guice在大部分时间都是很方便的,简单易用。Guice和Spring等其他容器的最大区别是,Guice相信注入大部分都是根据类型的,而不是根据名字的。Guice在使用上的方便,很大部分都来自于按类型注入。
Bind多个实现
但是,如果对于一个类型,我有多个实现怎么办?最常见的问题是,有两个数据库。
如果有两个数据库的话,这binding能怎么写呢?
这样写话,Guice会报告sqlMapClient已经存在一个binding了。而且在使用的地方
到底是注入了db1,还是db2?最直观的解决方案是给DB1和DB2不同的key做binding。有三个选择
选择一:不同的interface
选择二:Binding Annotation
选择三:Named binding
三种方案,没一种是完美的。第一种显然是非常繁琐的,而且使用的地方需要知道具体的类型。方案二要好很多,但是需要额外定义一个annotation,同时使用的时候需要知道自己需要的依赖是被什么annotation标记的。这就有一点serviceLocator.locateService(db1_connection)的味道了。第三种更加糟糕,使用的地方知道自己需要的依赖的名字,而且名字是一个字符串,不再是重构安全的了,也无法查找引用。
在Guice 2.0里,有了第四种方案。
选择四:Child Injector
使用不同的数据库,需要用不同的injector做注入。但是这种方式的限制其实更大,不是所有的系统都可以自然地分成多个分区,每个分区用不同的容器的。
综上,Guice有四种方式支持bind多个实现,Guice推荐的方式是使用bindingAnnotation。
Bind Set
这看起来是一个很简单问题,不就是这么写吗?
但是,我需要Integer和String两个不同的Set如何?也简单
或者可以利用Guice2里增加的Types这个类
看起来也挺简单的。但是如果这个Set的成员不是简单的一个String呢?比如我有一个接口叫OrderProcessor。
我需要不同的OrderProcessor来对Order做不同的处理(发邮件,存数据库)。分别对应的有:
这个时候,这个Set<OrderProcessor>怎么bind呢?我们不能这么做:
这么做的问题是new MailOrderProcessor()无法给它注入MailSender。而在Module内又无法用injector.getInstance(MailOrderProcessor.class)。对于这种情况,有四种选择:
选择一:用injectMemebers手工注入依赖
选择二:包装Set
选择三:getProvider
这里利用的特性是在AbstractModule内提供了一个方法叫getProvider。这样虽然在Module内无法调用injector.getInstance,但是可以获得instance的provider。这样,我们得到的是order processor的provider的set,而不直接拿到order processor的set。
选择四:getProvider + ProvidedOrderProcessor
这样,就可以把上面的Set<Provider<OrderProcessor>>变成Set<OrderProcessor>了。
多Module共同Bind一个Collection
要是Set<OrderProcessor>是由多个Module共同添加的又该怎么做?只是用Guice的Core,一个Binding必须在一个Module内完成。但是依赖于Guice的扩展Multibindings,就可以做到多个Module共同Bind一个Set。
似乎问题解决了?而且Multibindings扩展还支持Map。
限制
但是List怎么办?至今仍然没有官方的支持来解决这个问题。另外一个问题是如何Bind一个Chain,也就是Decorator模式。
如果有多个Decorator,形成了一条互相嵌套的Chain的话,就比较复杂了。对于一个Module的情况下,可以利用前面所说的ProvidedOrderProcessor的方式。但是如果要过个Module个共同来构建这个Chain的话,就没有官方做法了。
用可插拔的Module做为扩展点
理想状态是应用程序有XModule,YModule,ZModule。当没有ZModule的时候,应用程序仍然可以工作。但是插上了ZModule之后,一些行为就可以被扩展。这样就可以通过添加不同的模块来达到扩充的目的。怎么做呢?Guice提供了以下能力可以支持这样的效果。
方法一:@ImplementedBy, @ProvidedBy
这样当所有的Module都没有指定OrderProcessor的实现的时候,就会默认使用MailOrderProcessor。如果bind(OrderProcessor.class).to(DbOrderProcessor.class),则会把使用DbOrderProcessor。这个特性非常方便,尤其是有一些Service只会在测试时被改变的时候。
在所有的产品代码里,CurrentTimeProvider都会是DefaultImpl。但是在测试里,我们可以指定bind(CurrentTimeProvider.class).toInstance(new FixedTimeProvider(2008,5,12));这样时间就定格在那一刻了。
方法二:@Inject(optional = true)
这种方式适用于需要依赖的地方能够自己提供一个缺省实现的情况。如果标记processor为optional的,那么processor就变成了ProcessOrderService的一个扩展点。如果用户愿意,可以随时通过添加Module的方式,改变ProcessOrderService行为的目的。
方法三:Multibindings
前面我们已经看到了,通过Multibinder,可以用多个Module共同Bind一个Set。利用这个特性,就可以实现添加Module,添加不同的实现的目的。也是一个非常的实现扩展点的方式。
方法四:Override
这是Guice2新增的一个功能。使用很简单
这样,如果都是同样的key的话,DefaultModule里定义的binding就会被CustomizationModule给改写。这个特性非常暴力,把本来已经很复杂的binding解析过程变得更加的复杂了。要知道最终使用的binding是从哪个Module来的就更加困难了。一般来说不推荐使用。但是由于有了如此暴力的特性,使得多个Module共同Bind同一个List或者Decorator Chain变成可能了:
Before是一个Binding Annotation。在另外一个Module中只要重复bind CUSTOMIZABLE_KEY就可以了
使用的时候用Modules.override把缺省实现(DummyOrderProcessor)替换就可以了。
Bind多个实现
但是,如果对于一个类型,我有多个实现怎么办?最常见的问题是,有两个数据库。
bind(sqlMapClient.class).toInstance(createSqlMapClientForDB1());
如果有两个数据库的话,这binding能怎么写呢?
bind(sqlMapClient.class).toInstance(createSqlMapClientForDB1()); bind(sqlMapClient.class).toInstance(createSqlMapClientForDB2());
这样写话,Guice会报告sqlMapClient已经存在一个binding了。而且在使用的地方
public class Service1 { @Inject SqlMapClient sqlMapClient; }
到底是注入了db1,还是db2?最直观的解决方案是给DB1和DB2不同的key做binding。有三个选择
选择一:不同的interface
public class Db1SqlMapClient extends SqlMapClient { private final SqlMapClient delegate; // delegate all methods of sql map client } public class Service1 { @Inject Db1SqlMapClient sqlMapClient; }
选择二:Binding Annotation
@BindingAnnotation @Retention(RetentionPolicy.RUNTIME public @interface DB1 { } bind(SqlMapClient.class) .annotatedWith(DB1.class) .with(createSqlMapClientForDB1()); public class Service1 { @Inject @DB1 SqlMapClient sqlMapClient; }
选择三:Named binding
bind(SqlMapClient.class) .annotatedWith(Names.named("DB1")) .with(createSqlMapClientForDB1()); public class Service1 { @Inject @Named("DB1") SqlMapClient sqlMapClient; }
三种方案,没一种是完美的。第一种显然是非常繁琐的,而且使用的地方需要知道具体的类型。方案二要好很多,但是需要额外定义一个annotation,同时使用的时候需要知道自己需要的依赖是被什么annotation标记的。这就有一点serviceLocator.locateService(db1_connection)的味道了。第三种更加糟糕,使用的地方知道自己需要的依赖的名字,而且名字是一个字符串,不再是重构安全的了,也无法查找引用。
在Guice 2.0里,有了第四种方案。
选择四:Child Injector
db1Injector = injector.createChildInjector(new AbstractModule() { public void configure() { bind(SqlMapClient.class).toInstance(createSqlMapClientForDB1()); } }); db2Injector = injector.createChildInjector(new AbstractModule() { public void configure() { bind(SqlMapClient.class).toInstance(createSqlMapClientForDB2()); } });
使用不同的数据库,需要用不同的injector做注入。但是这种方式的限制其实更大,不是所有的系统都可以自然地分成多个分区,每个分区用不同的容器的。
综上,Guice有四种方式支持bind多个实现,Guice推荐的方式是使用bindingAnnotation。
Bind Set
这看起来是一个很简单问题,不就是这么写吗?
bind(Set.class).toInstance(new HashSet());
但是,我需要Integer和String两个不同的Set如何?也简单
bind(new TypeLiteral<Set<String>>(){}).toInstance(new HashSet<String>(){{ add("Hello"); add("World"); }});
{{}}是啥?呃,自己复习java语法去吧。
或者可以利用Guice2里增加的Types这个类
bind(Types.setOf(String.class)).toInstance(new HashSet<String>(){{ add("Hello"); add("World"); }});
看起来也挺简单的。但是如果这个Set的成员不是简单的一个String呢?比如我有一个接口叫OrderProcessor。
public interface OrderProcessor { void processOrder(Order order); }
我需要不同的OrderProcessor来对Order做不同的处理(发邮件,存数据库)。分别对应的有:
public class MailOrderProcessor implements OrderProcessor { @Inject EmailSender emailSender // send mail }
public class DbOrderProcessor implements OrderProcessor { @Inject SqlMapClient sqlMapClient; // save order to database }
这个时候,这个Set<OrderProcessor>怎么bind呢?我们不能这么做:
bind(Types.of(Set.class)).toInstance(new HashSet<OrderProcessor>(){{ add(new MailOrderProcessor()); add(new DbOrderProcessor()); }});
这么做的问题是new MailOrderProcessor()无法给它注入MailSender。而在Module内又无法用injector.getInstance(MailOrderProcessor.class)。对于这种情况,有四种选择:
选择一:用injectMemebers手工注入依赖
@Inject Injector injector; for (OrderProcessor orderProcessor : orderProcessors) { injector.injectMemebers(orderProcess); }
选择二:包装Set
public class OrderProcessors { private final Set<OrderProcessor> processors = new HashSet<OrderProcessor>(); @Inject public void setDbOrderProcessor(DbOrderProcessor processor) { processors.add(processor); } @Inject public void setMailOrderProcessor(MailOrderProcessor processor) { processors.add(processor); } }
选择三:getProvider
bind(new TypeLiteral<Set<Provider<OrderProcessor>>>(){}).toInstance(new HashSet<Provider<OrderProcessor>>(){{ add(getProvider(DbOrderProcessor.class); add(getProvider(MailOrderProcessor.class); }});
这里利用的特性是在AbstractModule内提供了一个方法叫getProvider。这样虽然在Module内无法调用injector.getInstance,但是可以获得instance的provider。这样,我们得到的是order processor的provider的set,而不直接拿到order processor的set。
选择四:getProvider + ProvidedOrderProcessor
public class ProvidedOrderProcessor implements OrderProcessor { private final Provider<OrderProcessor> provider; public ProvidedOrderProcessor(Provider<OrderProcessor> provider) { this.provider = provider; } public void processOrder(Order order) { provider.get().processOrder(order); } }
这样,就可以把上面的Set<Provider<OrderProcessor>>变成Set<OrderProcessor>了。
bind(Types.setOf(OrderProcessor.class)).toInstance(new HashSet<OrderProcessor>(){{ add(getLazyInstance(DbOrderProcessor.class)); add(getLazyInstance(MailOrderProcessor.class)); }}); OrderProcessor getLazyInstance(Class<? extends OrderProcessor> clazz) { return new ProvidedOrderProcessor(getProvider(clazz)); }
多Module共同Bind一个Collection
要是Set<OrderProcessor>是由多个Module共同添加的又该怎么做?只是用Guice的Core,一个Binding必须在一个Module内完成。但是依赖于Guice的扩展Multibindings,就可以做到多个Module共同Bind一个Set。
public class Module1 extends AbstractModule { public void configure() { Multibinder<OrderProcessor> multibinder = Multibinder.newSetBinder(binder(), OrderProcessor.class); multibinder.addBinding().to(MailOrderProcessor.class); } } public class Module2 extends AbstractModule { public void configure() { Multibinder<OrderProcessor> multibinder = Multibinder.newSetBinder(binder(), OrderProcessor.class); multibinder.addBinding().to(DbOrderProcessor.class); } }
似乎问题解决了?而且Multibindings扩展还支持Map。
限制
但是List怎么办?至今仍然没有官方的支持来解决这个问题。另外一个问题是如何Bind一个Chain,也就是Decorator模式。
public class DecoratedOrderProcessor implements OrderProcessor { private final OrderProcessor decorated; public OrderProcessor(OrderProcessor decorated) { this.decorated = decorated; } public void processOrder(Order order) { try { decorated.processOrder(order); } finally { // do something; } } }
如果有多个Decorator,形成了一条互相嵌套的Chain的话,就比较复杂了。对于一个Module的情况下,可以利用前面所说的ProvidedOrderProcessor的方式。但是如果要过个Module个共同来构建这个Chain的话,就没有官方做法了。
用可插拔的Module做为扩展点
理想状态是应用程序有XModule,YModule,ZModule。当没有ZModule的时候,应用程序仍然可以工作。但是插上了ZModule之后,一些行为就可以被扩展。这样就可以通过添加不同的模块来达到扩充的目的。怎么做呢?Guice提供了以下能力可以支持这样的效果。
方法一:@ImplementedBy, @ProvidedBy
@ImplementedBy(MailOrderProcessor.class) public interface OrderProcessor { }
这样当所有的Module都没有指定OrderProcessor的实现的时候,就会默认使用MailOrderProcessor。如果bind(OrderProcessor.class).to(DbOrderProcessor.class),则会把使用DbOrderProcessor。这个特性非常方便,尤其是有一些Service只会在测试时被改变的时候。
@ImplementedBy public interface CurrentTimeProvider { DateTime getNow(); public static class DefaultImpl implements CurrentTimeProvider { public DateTime getNow() { return new DateTime(); } } }
在所有的产品代码里,CurrentTimeProvider都会是DefaultImpl。但是在测试里,我们可以指定bind(CurrentTimeProvider.class).toInstance(new FixedTimeProvider(2008,5,12));这样时间就定格在那一刻了。
方法二:@Inject(optional = true)
public class ProcessOrderService { @Inject(Optional = true) OrderProcessor processor = new DummyOrderProcessor(); }
这种方式适用于需要依赖的地方能够自己提供一个缺省实现的情况。如果标记processor为optional的,那么processor就变成了ProcessOrderService的一个扩展点。如果用户愿意,可以随时通过添加Module的方式,改变ProcessOrderService行为的目的。
方法三:Multibindings
前面我们已经看到了,通过Multibinder,可以用多个Module共同Bind一个Set。利用这个特性,就可以实现添加Module,添加不同的实现的目的。也是一个非常的实现扩展点的方式。
方法四:Override
这是Guice2新增的一个功能。使用很简单
Module finalModule = Modules .override(new DefaultModule()) .with(new CustomizationModule());
这样,如果都是同样的key的话,DefaultModule里定义的binding就会被CustomizationModule给改写。这个特性非常暴力,把本来已经很复杂的binding解析过程变得更加的复杂了。要知道最终使用的binding是从哪个Module来的就更加困难了。一般来说不推荐使用。但是由于有了如此暴力的特性,使得多个Module共同Bind同一个List或者Decorator Chain变成可能了:
Key CUSTOMIZABLE_KEY = Key.get(OrderProcessor.class, new Before(MailOrderProcessor.class)); bind(Types.listOf(OrderProcessor.class)).toInstance(new ArrayList<OrderProcessor>(){{ add(new ProvidedOrderProcessor(getProvider(CUSTOMIZABLE_KEY)); add(getLazyInstance(MailOrderProcessor.class); }}); bind(CUSTOMIZABLE_KEY).toInstance(new DummyOrderProcessor());
Before是一个Binding Annotation。在另外一个Module中只要重复bind CUSTOMIZABLE_KEY就可以了
bind(CUSTOMIZABLE_KEY).to(getLazyInstance(DbOrderProcessor.class));
使用的时候用Modules.override把缺省实现(DummyOrderProcessor)替换就可以了。
评论
2 楼
recoco
2010-01-15
最近在看 juice,多谢楼主的资料。
1 楼
fansofjava
2009-06-16
还有一个加载配置文件的问题,原来官方一直称支持XML,研究了半天,
发现还是只支持properties文件加载,后来再看,官方干脆不提了,晕
虽然搞了两年多,其实2.0并没有什么大的改进
不过也不错了,它的核心代码就两个包,也就700多K的样子,也不依赖其它的包,这一点的确值得称道
发现还是只支持properties文件加载,后来再看,官方干脆不提了,晕
虽然搞了两年多,其实2.0并没有什么大的改进
不过也不错了,它的核心代码就两个包,也就700多K的样子,也不依赖其它的包,这一点的确值得称道
发表评论
-
再论领域模型的困境
2009-06-03 18:54 2077距离上次发帖讨论领域 ... -
让AnnotationConfiguration支持自定义UserCollectionType
2008-12-09 17:45 2788在Hibernate的Jira上,这个两个issue已经放了很 ... -
领域模型的价值与困境
2008-11-27 23:23 3954很久以前大家就关于这个方面有很多讨论了。前两天我又挖了一个坑来 ... -
你所不知道的CommandBar
2008-10-29 08:40 2913Office能够让你写插件。2 ... -
关于Estimation的随笔
2008-10-27 09:04 1141Estimation有很多流派。 从数字的选择上来看:有的人喜 ... -
Domain Model - The Theory, The Reality, The Dream
2008-08-10 21:25 1285梦想照进现实 -
关于estimation的闲言碎语
2008-07-11 09:06 1507estimation只是一个开始,不是结束.好的estim ... -
let's placeBid
2008-05-12 10:11 1510这个例子很老啦,在之 ... -
贫血的Domain Model
2008-05-09 00:18 5300好老的话题啦。拿出来炒炒冷饭。各位见谅。 —————————— ... -
图形界面自动化测试
2008-05-04 22:16 2064Windows Win32 API (pywinauto, a ... -
the paint points of xaml
2008-01-16 08:56 1495Pain Point 1: XAML always creat ... -
lessons we have learnt about office integration
2008-01-16 08:54 1425Lesson 1: trust it Everything ... -
Outlook MAPIOBJECT
2008-01-09 18:06 2076Outlook的对象模型中,很多对象都有一个MAPIOBJEC ... -
.NET Remoting Callback
2007-10-12 20:42 2238有三个主要的障碍: 1、服务器解析不到客户端的assembly ... -
企业应用开发者使用WPF的三个理由
2007-05-16 23:02 5760让控件更灵活的Data Template ... -
用UIAutomation做验收测试
2007-05-16 22:19 4556这是被测的应用程序: 应用.NET 3.0提供的UIA ... -
mock框架搞什么搞?
2007-05-11 11:11 13528今天早上一时兴起,去网上下载下来JMock,EasyMock最 ... -
主动重构 => 被动重构
2007-05-10 16:34 2010引言 最近杂七杂八地思考了不少东西。但是很惊异地发现这三三两 ... -
我的酒窝.NET
2007-04-30 16:59 2767ajoo同学的酒窝有.NET版本啦! 项目主页: http: ... -
Naive Container 发布1.0版本
2007-04-29 17:50 2592二进制文件和源代码可以从这里下载到: http://naive ...
相关推荐
guice.jar guice.jar guice.jar guice.jar guice.jar guice.jar guice.jar
4. **高级特性**: 讲解 Guice 的高级功能,如绑定超类型、绑定注解、绑定条件等。 5. **实战案例**: 提供实际的代码示例,演示如何在项目中应用 Guice。 通过阅读文档和运行示例,开发者可以快速理解和掌握 Guice ...
### Guice用户中文指南 #### 一、简介 Guice是一个专门为Java 5及后续版本设计的超轻量级依赖注入框架。...此外,Guice还支持多种高级功能,如作用域管理、自定义绑定等,使其成为Java应用中依赖注入的强大工具之一。
这个“基于Guice的HelloWorld项目”就是一个简单的示例,用于展示如何使用Guice进行依赖注入。 首先,我们需要理解Guice的核心概念——Injector。Injector是Guice的核心类,它负责创建对象并管理它们的生命周期。...
这使得Guice可以控制Action实例的生命周期,以及依赖注入到Action中,增强了Struts2的应用可测试性和可维护性。 4. **Guice的优势**: - **轻量级**:相比Spring等其他IoC容器,Guice更小巧,引入的依赖少,启动...
4. **提供者(Providers)**:Guice允许我们使用`Provider`接口来自定义对象的创建过程,这在某些需要特殊初始化或生命周期管理的情况下非常有用。 5. **注解处理(Annotation Processing)**:Guice使用Java的注解处理...
接下来,可以通过创建一个 Guice Injector 并从中获取 SecurityManager 实例来使用这个模块: ```java Injector injector = Guice.createInjector(new MyShiroModule()); SecurityManager securityManager = ...
本书《Google Guice: Agile Lightweight Dependency Injection Framework》旨在深入探讨Guice的核心概念和技术细节,并通过丰富的示例和背景信息帮助读者全面掌握这一强大的工具。 #### 二、Guice概述 Guice是由...
1. `MyModule.java`: 这个文件代表了一个Guice模块,模块是Guice中定义服务和其绑定的地方。例如,我们可以在这里声明`MyService`接口的具体实现`MyServiceImpl`,并告诉Guice在需要`MyService`时,应该使用`...
5. **类型监听器(Type Listeners)**:Guice允许我们注册类型监听器,监听特定类型的实例创建事件,这在需要在对象创建后执行某些操作时非常有用。 6. **绑定选项**:Guice提供了多种绑定方式,如绑定到类、绑定到...
这可以使得 Client 对象更加灵活和可维护。 Guice 的优越性体现在以下几个方面: 1. 易于测试:使用 Guice,我们可以非常容易地进行单元测试。我们可以使用 Guice 来注入 Mock 对象,以便测试 Client 对象的行为。...
赠送jar包:guice-4.0.jar; 赠送原API文档:guice-4.0-javadoc.jar; 赠送源代码:guice-4.0-sources.jar; 赠送Maven依赖信息文件:guice-4.0.pom; 包含翻译后的API文档:guice-4.0-javadoc-API文档-中文(简体)版...
### Google Guice 用户手册知识点详解 #### 一、Google Guice 概览 **Google Guice** 是一个轻量级的 Java 依赖注入容器,它...对于那些寻求更高级、更灵活的依赖注入框架的开发者来说,Guice 是一个值得考虑的选择。
Guice,全称为Google Guice,是一款轻量级的依赖注入框架,由Google开发并开源。依赖注入(Dependency Injection,DI)...同时,阅读博文(链接已给出)会提供更深入的指导和实际案例,以便进一步探索Guice的高级特性。
guice 学习资料,快速掌握guice的编程技巧以及了解其机制。
总的来说,Google Guice是一个强大的依赖注入框架,通过使用Guice-3.0.jar和javax.inject.jar这两个库,开发者可以有效地组织和管理Java应用程序的组件,提高代码质量,降低维护成本。无论是小型项目还是大型企业级...
“Java on Guice”这篇文档探讨了依赖注入(Dependency Injection, DI)在Java应用中的实践方式及其带来的诸多好处。它由Google的Bob Lee和Kevin Bourrillion撰写,并于2007年4月26日发布。文档通过一系列问题引导...
在阅读《初试Guice》这篇博客文章时,你可能会学习到如何设置Guice模块,如何声明和注入依赖,以及如何在项目中集成和使用Guice。作者可能会分享一些最佳实践和常见陷阱,帮助读者更好地理解和应用Guice。此外,文章...