最近看了一点Dependency Injection的东西, 发现很难理解为什么要使用DI, 为什么要IoC, 到底能带来什么好处. 于是决定选择一款框架学习一下, 以增进自己的理解. 比较之后, 选择了Google Guice, 主要还是从如何使用入手.
1. Guice简介
Guice是Google开发的一款轻量级的注入框架(a lightweight dependency injection framework for Java 5 and above), 与大名鼎鼎的Spring相比, 确实是轻量级的框架. 其主要目标是替代开发中使用关键字new或者大量使用的工厂方法, 以达到解除耦合的目的; 同样该框架的作者Bob Lee也希望Guice能给使用者带来更好的体验, 能帮助开发者提高开发速度和方便维护. OK, 废话不多说了. 详细的介绍可以去官网查看: http://code.google.com/p/google-guice/
2. Hello Guice
我使用的参考书为Apress的Google Guice(Agile Lightweight Dependency Injection Framework), 是Google官网Slice的推荐用书. 该书使用了一个FortuneCookie的例子, 非常形象的介绍了Guice的使用方法. 在本文, 参考介绍使用贩卖Juice的VendingMachine作为例子.
在科技发展初期, 一切都是那么简单.
我们有一个只贩卖一种果汁的自动贩卖机. 为此我们提供了一个接口, 因为世界上不止这么简单的一种贩售机, 除了果汁, 还能卖点别的东西. 而自动贩卖机的果汁也很简单, 只包含一个价格的属性, 当然果汁的味道还是不错的.
public interface VendingMachine { public int showPrice(); public Juice sellJuice(); } import java.util.Arrays; import java.util.List; public class VendingMachineImpl implements VendingMachine { private static final List KINDS_OF_JUICE = Arrays.asList( new Juice(100) ); @Override public int showPrice() { return KINDS_OF_JUICE.get(0).getPrice(); } @Override public Juice sellJuice() { return KINDS_OF_JUICE.get(0); } } public class Juice { private int price; public Juice(int price) { this.price = price; } public int getPrice() { return price; } public String toString() { return "It tastes good!"; } }
那么作为消费者Consumer, 可以询问价格并且购买果汁. 当然购买果汁需要自动贩售机提供服务.
import com.google.inject.Inject; public class Consumer { private VendingMachine vendingMachine; @Inject public Consumer(VendingMachine vendingMachine) { this.vendingMachine = vendingMachine; } public int getPrice() { return vendingMachine.showPrice(); } public Juice buyJuice() { return vendingMachine.sellJuice(); } }
在构造函数上我们添加了一个@Inject的标记, 这个是Guice的标记, 表示在构造函数进行Inject, 再加上Module, 我们就可以把VendingMachine和VendingMachineImpl粘合起来. Module是Guice提供的一个接口, 是其指定实现方式的一种方法, 也是比较推荐的一种方式. 绑定的语法可以这么来读: 绑定VendingMachine到VendingMachineImpl的实现上, 使用单例模式. 在Module中进行绑定后, 在标记为@Inject的地方, Guice就会自动为其指定一个实现的实例.
import com.google.inject.Binder; import com.google.inject.Module; import com.google.inject.Scopes; public class VendingMachineModule implements Module { @Override public void configure(Binder binder) { binder.bind(VendingMachine.class) .to(VendingMachineImpl.class) .in(Scopes.SINGLETON); } }
万事俱备, 只欠东风. 接下来该喝点果汁了. 通过Guice.createInjector使用我们的Module定义创建出一个Injector后, 我们就可以通过getInstance方法来获取各种对象, 从此告别new和工厂方法.
public class Bootstrap { public static void main(String[] args) { Injector injector = Guice.createInjector(new VendingMachineModule()); Consumer consumer = injector.getInstance(Consumer.class); System.out.println("The price of juice is " + consumer.getPrice()); System.out.println("So, buy this juice and drink it: " + consumer.buyJuice()); } }
以上为第一个Guice的例子, 我们可以简单的总结出最基本的使用方法: 通过Module配置抽象与实现的关系, 通过@Inject标签来标记注入点. 然后使用万能的Injector来获得对象. 应该是一个很明确的思路, 使用起来也比较简洁.
但是问题随之而来:
1. 这样做能为我们带来什么好处呢?
2. 这就是传说中的Dependency Injection, 该如何去解释.
3. 我们如何能提供更多的服务(More Juice).
4. 我们还有一台售卖零食的自动贩卖机, 我们如何能在买到果汁之后, 再去购买些零食呢?
后面的学习将继续对Guice框架的细节进行学习, 并围绕以上问题来做一些思考.