Google Guice之牛刀小试

Google Guice由google推出的一开源软件,是超轻量级的,下一代的,为Java 5及后续版本设计的依赖注入容器,其功能类似于如日中天的Spring。

下面我们就来了解一下Guice,在此之前,先看一个官方例子:在应用程序中,要把所有的东西装配起来是一件很乏味的事件,这要涉及到连接数据,服务,表现层类等方面,这是一个比萨饼订购网站的计费代码例子用于这些方面的对比。

public interface BillingService {

  /**
   * Attempts to charge the order to the credit card. Both successful and
   * failed transactions will be recorded.
   *
   * @return a receipt of the transaction. If the charge was successful, the
   *      receipt will be successful. Otherwise, the receipt will contain a
   *      decline note describing why the charge failed.
   */
  Receipt chargeOrder(PizzaOrder order, CreditCard creditCard);
}

BillingService的实现类,我们会用单元测试进行测试,剩下的我们需要一个FakeCreditCardProcessor来避免其直接与CreditCard打交道,这是面向对象中封装的表现。

第一种实现方式:直接调用构造方法:

public class RealBillingService implements BillingService {
  public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {
    CreditCardProcessor processor = new PaypalCreditCardProcessor();//构造方法创建CreditCardProcessor
    TransactionLog transactionLog = new DatabaseTransactionLog();//构造方法创建TransactionLog对象

    try {
      ChargeResult result = processor.charge(creditCard, order.getAmount());
      transactionLog.logChargeResult(result);

      return result.wasSuccessful()
          ? Receipt.forSuccessfulCharge(order.getAmount())
          : Receipt.forDeclinedCharge(result.getDeclineMessage());
     } catch (UnreachableException e) {
      transactionLog.logConnectException(e);
      return Receipt.forSystemFailure(e.getMessage());
    }
  }
}

这样的代码缺乏模块性与可测试性,因为这在编译期就直接依赖了CreditCardProcessor实现类,耦合性太强。

第二种实现方式:使用工厂模式:

使用一个工厂类可以使客户端与实现解耦,一个简单工厂使用一静态方法来获取或设置接口实现,下面是一样版:

public class CreditCardProcessorFactory {

  private static CreditCardProcessor instance;

  public static void setInstance(CreditCardProcessor creditCardProcessor) {
    instance = creditCardProcessor;
  }

  public static CreditCardProcessor getInstance() {
    if (instance == null) {
      return new SquareCreditCardProcessor();
    }

    return instance;
  }
}

在客户端代码中,只需要使用工厂类把new关键字替换就行了:

public class RealBillingService implements BillingService {
  public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {
    CreditCardProcessor processor = CreditCardProcessorFactory.getInstance();
    TransactionLog transactionLog = TransactionLogFactory.getInstance();

    try {
      ChargeResult result = processor.charge(creditCard, order.getAmount());
      transactionLog.logChargeResult(result);

      return result.wasSuccessful()
          ? Receipt.forSuccessfulCharge(order.getAmount())
          : Receipt.forDeclinedCharge(result.getDeclineMessage());
     } catch (UnreachableException e) {
      transactionLog.logConnectException(e);
      return Receipt.forSystemFailure(e.getMessage());
    }
  }
}

在使用了工厂模式后的单元测试:

public class RealBillingServiceTest extends TestCase {

  private final PizzaOrder order = new PizzaOrder(100);
  private final CreditCard creditCard = new CreditCard("1234", 11, 2010);

  private final InMemoryTransactionLog transactionLog = new InMemoryTransactionLog();
  private final FakeCreditCardProcessor creditCardProcessor = new FakeCreditCardProcessor();

  @Override public void setUp() {
    TransactionLogFactory.setInstance(transactionLog);
    CreditCardProcessorFactory.setInstance(creditCardProcessor);
  }

  @Override public void tearDown() {
    TransactionLogFactory.setInstance(null);
    CreditCardProcessorFactory.setInstance(null);
  }

  public void testSuccessfulCharge() {
    RealBillingService billingService = new RealBillingService();
    Receipt receipt = billingService.chargeOrder(order, creditCard);

    assertTrue(receipt.hasSuccessfulCharge());
    assertEquals(100, receipt.getAmountOfCharge());
    assertEquals(creditCard, creditCardProcessor.getCardOfOnlyCharge());
    assertEquals(100, creditCardProcessor.getAmountOfOnlyCharge());
    assertTrue(transactionLog.wasSuccessLogged());
  }
}

这样代码还是有点笨拙,一个全局变量保存了实现实例,这样我们要非常小心该变量的赋值与值释放,如果tailDown方法失败了,

全局变量仍然有效,这可能就会给其它的测试带来问题,这样还不能并行运行多个测试用例。最大的问题在于,随着应用的扩大,

有新的依赖的时候就会出现越来越多的工厂类,使应用效率下降。

第三种方式:依赖注入

像工厂模式一样,依赖注入也是一种设计模式,其主要原则是将行为与依赖分离开来,在上面的例子中RealBillingService不负责TransactionLog与CreditCardProcessor对象的创建,换之的是这两个对象在RealBillingService的构造方法参数中传递进来。

public class RealBillingService implements BillingService {
  private final CreditCardProcessor processor;
  private final TransactionLog transactionLog;

  public RealBillingService(CreditCardProcessor processor,
      TransactionLog transactionLog) {
    this.processor = processor;
    this.transactionLog = transactionLog;
  }

  public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {
    try {
      ChargeResult result = processor.charge(creditCard, order.getAmount());
      transactionLog.logChargeResult(result);

      return result.wasSuccessful()
          ? Receipt.forSuccessfulCharge(order.getAmount())
          : Receipt.forDeclinedCharge(result.getDeclineMessage());
     } catch (UnreachableException e) {
      transactionLog.logConnectException(e);
      return Receipt.forSystemFailure(e.getMessage());
    }
  }
}

这样,我们不需要任何的工厂类,还可以移除setUp与tearDown方法来简化单元测试:

public class RealBillingServiceTest extends TestCase {

  private final PizzaOrder order = new PizzaOrder(100);
  private final CreditCard creditCard = new CreditCard("1234", 11, 2010);

  private final InMemoryTransactionLog transactionLog = new InMemoryTransactionLog();
  private final FakeCreditCardProcessor creditCardProcessor = new FakeCreditCardProcessor();

  public void testSuccessfulCharge() {
    RealBillingService billingService
        = new RealBillingService(creditCardProcessor, transactionLog);
    Receipt receipt = billingService.chargeOrder(order, creditCard);

    assertTrue(receipt.hasSuccessfulCharge());
    assertEquals(100, receipt.getAmountOfCharge());
    assertEquals(creditCard, creditCardProcessor.getCardOfOnlyCharge());
    assertEquals(100, creditCardProcessor.getAmountOfOnlyCharge());
    assertTrue(transactionLog.wasSuccessLogged());
  }
}

现在不幸的是,BillingService的客户端需要创建它的依赖,现在最好是有一框架来自动创建这些依赖,不然我们就要手动地去创建这些循环依赖。

现在到Guice出场的时候,使用Guice进行依赖注入

依赖注入模式可以让代码更具模块性,更易于测试,而且Guice使其易于编写。在上面的计费例子中,我们第一步要告诉Guice怎么映射接口与实现类,这是通过Guice的Module进行配置的,它可以是任何一个实现了Module接口的Java类。

public class BillingModule extends AbstractModule {
  @Override
  protected void configure() {
    bind(TransactionLog.class).to(DatabaseTransactionLog.class);//将接口与实现进行映射绑定
    bind(CreditCardProcessor.class).to(PaypalCreditCardProcessor.class);
    bind(BillingService.class).to(RealBillingService.class);
  }
}

当进行依赖注入的时候,对象在它们的构造参数中接收依赖。要创建一个对象,必须先创建出它的依赖,但是要创建每一个依赖,就要创建依赖的每一个依赖,如此往复。所以当你创建一个对象的时候真正要创建的是一张对象图。手动创建一张对象图是费劳力的,趋于错误的,而且使测试变得困难。好在Guice可以为我们创建这张对象图,而我们要做的就是进行配置告诉它如果去准确地创建这张对象图。

在RealBillingService的构造方法中添加@Inject注解,Guice会检查添加了注解的构造方法,并为每一个参数查找值。

添加@Inject注解就是在进行配置式作,告诉Guice如果创建对象图,当然@Inject注解不仅可以放置于构造方法上,也可以放置于setter方法与字段上。

public class RealBillingService implements BillingService {
  private final CreditCardProcessor processor;
  private final TransactionLog transactionLog;

  @Inject
  public RealBillingService(CreditCardProcessor processor,
      TransactionLog transactionLog) {
    this.processor = processor;
    this.transactionLog = transactionLog;
  }

  public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {
    try {
      ChargeResult result = processor.charge(creditCard, order.getAmount());
      transactionLog.logChargeResult(result);

      return result.wasSuccessful()
          ? Receipt.forSuccessfulCharge(order.getAmount())
          : Receipt.forDeclinedCharge(result.getDeclineMessage());
     } catch (UnreachableException e) {
      transactionLog.logConnectException(e);
      return Receipt.forSystemFailure(e.getMessage());
    }
  }
}

最后,我们将这些整合在一起如下,Injector类用于获取任何绑定类的实例:

public static void main(String[] args) {
	Injector injector = Guice.createInjector(new BillingModule());
	BillingService billingService = injector.getInstance(BillingService.class);
	...
}
时间: 2024-10-03 23:35:12

Google Guice之牛刀小试的相关文章

Google Guice 系列教程 - 基础实践

转载:http://www.cnblogs.com/youngC/archive/2012/12/21/2828419.html 前言 Google Guice 是一个轻量级的依赖注入框架,它支持Java 5或者更高版本的JDK,得利于Java 5中提供的泛型 (Generics) 和注释 (Annotations) ,它可以使得代码类型安全 (type-safe) .那么何时使用在代码中使用 Guice 进行注入呢?一般来说,如果在你的应用代码中业务对象 (Business Objects)

Google Guice 入门教程

01 - 依赖注入 1. 依赖注入 1.1 类依赖注入 所谓的绑定就是将一个接口绑定到具体的类中,这样客户端不用关心具体的实现,而只需要获取相应的接口完成其服务即可. HelloWorld.java 1     public interface HelloWorld { 2 3         String sayHello(); 4     } 5 然后是具体的实现,HelloWorldImpl.java 1     public class HelloWorldImpl implements

超轻量级DI容器框架Google Guice与Spring框架的区别教程详解及其demo代码片段分享

原创不易,转载请注明出处:超轻量级DI容器框架Google Guice与Spring框架的区别教程详解及其demo代码片段分享 代码下载地址:http://www.zuidaima.com/share/1759689106541568.htm 依赖注入,DI(Dependency Injection),它的作用自然不必多说,提及DI容器,例如spring,picoContainer,EJB容器等等,近日,google诞生了更轻巧的DI容器--Guice! 废话不多讲了,先看看Guice是如何实现

jdbc框架 commons-dbutils+google guice+servlet 实现一个例子

最近闲着无聊,于是看了一下jdbc框架 commons-dbutils与注入google guice. 我就简单的封装了一下代码,效率还是可以的.... jdbc+google guice+servlet 的web实现: http://pan.baidu.com/s/1i4OpyTJ 觉得不错点个赞...hello world!

史上最好用的依赖注入框架Google Guice【转】

Guice是Google开发的一个轻量级,基于Java5(主要运用泛型与注释特性)的依赖注入框架(IOC).Guice非常小而且快. (其他的依赖注入框架还有Dagger,Spring) Spring框架的依赖注入是家喻户晓的,但是在实际的开发中我们想使用便捷的依赖注入功能,但是又不想引入Spring框架的复杂性,该怎么办呢? 有了Google Guice,这个问题便简单了,首先在你的maven项目里引入 <dependency> <groupId>com.google.injec

开源介绍:Google Guava、Google Guice、Joda-Time

一.Guava 是一个 Google 的基于java1.6的类库集合的扩展项目,包括 collections, caching, primitives support, concurrency libraries, common annotations, string processing, I/O, 等等. 这些高质量的 API 可以使你的JAVa代码更加优雅,更加简洁,让你工作更加轻松愉悦.下面我们就开启优雅Java编程学习之旅! 项目相关信息: 官方首页:http://code.googl

Google Guice

Guice (读作"juice")是超轻量级的,下一代的,为Java 5及后续版本设计的依赖注入容器. 简介 Java企业应用开发社区在连接对象方面花了很大功夫.你的Web应用如何访问中间层服务?你的服务如何连接到登录用户和事务管理器?关于这个问题你会发现很多通用的和特定的解决方案.有一些方案依赖于模式,另一些则使用框架.所有这些方案都会不同程度地引入一些难于测试或者程式化代码重复的问题.你马上就会看到,Guice 在这方面是全世界做得最好的:非常容易进行单元测试,最大程度的灵活性和可

google guice @inject comments

refer this document: http://blog.chinaunix.net/uid-20749563-id-718418.html @Inject注入方式,用@Inject来标识那个方法被注入 @ImplementedBy(Class)的注释方式.可以直接从你的接口指向一个缺省的实现,而省略掉对com.google.inject.Module的实现.其实这样就违背了多态的原则,一般使用较少,最后还是把控制权交给Module来处理. 当 Guice 找到注释时,它会挑选构造函数参

Google Guice之绑定方式

在Guice中,注入器的工作是装配对象图,当请求某一类型实例时,注入器根据对象图来判断如何创建实例.解析依赖.要确定如何解析依赖就需要通过配置注入器的绑定方式. 要创建绑定(Binding)对象,可以继承自AbstractModule类,然后覆盖其configure方法,在方法调用bind()方法来指来定每一次绑定,这些方法带有类型检查,如果你使用了错误的类型编译器就会报告编译错误.如果你已经写好了Module类,则创建一个Module类对象作为参数传递给Guice.createInjector