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;  }}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 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不负责TransactionLogCreditCardProcessor对象的创建,换之的是这两个对象在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());    }  }}

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

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);    ...}

-------------------------------- END -------------------------------

及时获取更多精彩文章,请关注公众号《Java精讲》。

©著作权归作者所有:来自51CTO博客作者Java精讲的原创作品,如需转载,请注明出处,否则将追究法律责任

好知识,才能预见未来

赞赏

0人进行了赞赏支持

更多相关文章

  1. 使用代码检查Dynamics 365中的备用键状态
  2. Kubernetes使用 S3FS 将 AWS S3 作为持久性存储
  3. Shiro源码分析---FilterChain创建过程
  4. 监控系统项目实施--安装与部署-- MySQL数据库--分区表创建
  5. 监控系统项目实施--安装与部署-- MySQL数据库--分区表创建2
  6. 使用CLI创建VPC
  7. 5s!用浏览器打造一个开箱即用的Linux系统
  8. AWS Elasticsearch Service 建立snapshot
  9. 软件供应链***(依赖关系混淆***)正在破坏你的系统安全

随机推荐

  1. 如何格式化Gtk.Entry中的条目
  2. 在matplotlib中如何使用不同的edgecolor
  3. python的全局变量与局部变量实验
  4. python实现邮件发送功能
  5. 常见的爬虫分析库(4)-爬虫之PyQuery
  6. Python学习笔记18:Python多线程编程
  7. 运维利器:钉钉机器人脚本告警(Linux Python
  8. 套接字。接受错误24:对许多打开的文件
  9. python打印列表的下标和值的例子:
  10. windows下,python的各类包的安装(cython, n