Google Guice学习

时间:2022-10-07 20:21:50

学习动力:公司项目使用

官方文档:https://github.com/google/guice/wiki/Motivation

学习阶段:入门

主要部份:

  1. 简介
  2. Bindings方式
  3. Scopes设定
  4. Injecting Providers
  5. 最佳实践 

简介

Guice通过注解方式提供以来注入,一种方式是通过@Injuct注入构造函数

class BillingService {
private final CreditCardProcessor processor;
private final TransactionLog transactionLog; @Inject
BillingService(CreditCardProcessor processor,
TransactionLog transactionLog) {
this.processor = processor;
this.transactionLog = transactionLog;
} public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {
...
}
}

然后通过Module来绑定接口与实现类

public class BillingModule extends AbstractModule {
@Override
protected void configure() { /*
* This tells Guice that whenever it sees a dependency on a TransactionLog,
* it should satisfy the dependency using a DatabaseTransactionLog.
*/
bind(TransactionLog.class).to(DatabaseTransactionLog.class); /*
* Similarly, this binding tells Guice that when CreditCardProcessor is used in
* a dependency, that should be satisfied with a PaypalCreditCardProcessor.
*/
bind(CreditCardProcessor.class).to(PaypalCreditCardProcessor.class);
}
}

最后通过Injector.getInstance(),通过接口类和Module获得实现类,减少了和具体类之间的耦合,让代码结构更加稳定,易于测试。

public static void main(String[] args) {
/*
* Guice.createInjector() takes your Modules, and returns a new Injector
* instance. Most applications will call this method exactly once, in their
* main() method.
*/
Injector injector = Guice.createInjector(new BillingModule()); /*
* Now that we've got the injector, we can build objects.
*/
BillingService billingService = injector.getInstance(BillingService.class);
...
}

Bindings方式

1.Linked Bindings方式

Linked Bindings将一个类型与它的实现类绑定。甚至可以将一个具体类型与它的子类绑定。绑定关系可以串联。

public class BillingModule extends AbstractModule {
@Override
protected void configure() {
bind(TransactionLog.class).to(DatabaseTransactionLog.class);
bind(DatabaseTransactionLog.class).to(MySqlDatabaseTransactionLog.class);
}
}

2.Binding Annotations方式

有时候我们需要将多个实现类绑定到同一个类型上面。比如在BillingService中,我们需要将PayPal credit card processor和Google Checkout processor 绑定到同一个CreditCardProcessor类型上。  Binding Annotations提供几种方式来实现,项目中使用比较多的是这种@Named。我们在需要注入的地方这样写:

public class RealBillingService implements BillingService {

  @Inject
public RealBillingService(@Named("Checkout") CreditCardProcessor processor,
TransactionLog transactionLog) {
...
}

然后在Module中这样绑定:

    bind(CreditCardProcessor.class)
.annotatedWith(Names.named("Checkout"))
.to(CheckoutCreditCardProcessor.class);

3.Instance Bindings

对于无法继承的类(比如String和Integer),可以使用toInstance()来绑定。

    bind(String.class)
.annotatedWith(Names.named("JDBC URL"))
.toInstance("jdbc:mysql://localhost/pizza");
bind(Integer.class)
.annotatedWith(Names.named("login timeout seconds"))
.toInstance(10);

4.@Provides Methods

使用@Provides可以通过创建一个方法来提供所需类型的实现,当需要这个类型的时候,injector就会调用这个方法。

public class BillingModule extends AbstractModule {
@Override
protected void configure() {
...
} @Provides
TransactionLog provideTransactionLog() {
DatabaseTransactionLog transactionLog = new DatabaseTransactionLog();
transactionLog.setJdbcUrl("jdbc:mysql://localhost/pizza");
transactionLog.setThreadPoolSize(30);
return transactionLog;
}
}

5.Provider Bindings

当@Provides 方法越来越复杂的时候,你会考虑使用一个Provider类来管理。Guice提供了Provider这个接口。Provider自身可能也有需要注入的组件。

下面是一个例子

public class DatabaseTransactionLogProvider implements Provider<TransactionLog> {
private final Connection connection; @Inject
public DatabaseTransactionLogProvider(Connection connection) {
this.connection = connection;
} public TransactionLog get() {
DatabaseTransactionLog transactionLog = new DatabaseTransactionLog();
transactionLog.setConnection(connection);
return transactionLog;
}
}

然后我们使用toProvider()将Provider和需要注入的类型绑定。

public class BillingModule extends AbstractModule {
@Override
protected void configure() {
bind(TransactionLog.class)
.toProvider(DatabaseTransactionLogProvider.class);
}

6.Untargeted Bindings

当需要绑定具体类,或是使用@ImplementedBy和@ProvidedBy的类型时,可以直接bind(),不加上to()

    bind(MyConcreteClass.class);
bind(AnotherConcreteClass.class).in(Singleton.class);

7.Constructor Bindings

当需要注入的对象来自第三方库,或是有多个构造函数的时候,我们不能使用@Injuct,这时可以使用@Provides可以解决问题!

但是因为一个什么原因(我没看懂),这也提供了一种toConstructor()来绑定。

public class BillingModule extends AbstractModule {
@Override
protected void configure() {
try {
bind(TransactionLog.class).toConstructor(
DatabaseTransactionLog.class.getConstructor(DatabaseConnection.class));
} catch (NoSuchMethodException e) {
addError(e);
}
}
}

Scopes设定

依赖注入框架既然帮忙注入对象,自然也要管理对象的生命周期Scope。Guice中通过注解来设定对象的Scope。

Guice中典型的生命周期有for the lifetime of an application (@Singleton), a session (@SessionScoped), or a request (@RequestScoped)

可以在相应的实现类上标注。

@Singleton
public class InMemoryTransactionLog implements TransactionLog {
/* everything here should be threadsafe! */
}

或是通过跟在bind()后的in()来设定。

bind(TransactionLog.class).to(InMemoryTransactionLog.class).in(Singleton.class);

或是在@Provides方法上标注。

  @Provides @Singleton
TransactionLog provideTransactionLog() {
...
}

当类型上的Scope和bind()方法后in()的Scope冲突时,bind()的Scope会被使用。

还有一些比较复杂的概念,延迟加载,需要深入理解的时候在再看吧。

Injecting Providers

我理解这一章的内容是使用Provider来替代接口作为注入的入口的好处。

当你需要多次获得一个新的注入对象时,使用Provider

public class LogFileTransactionLog implements TransactionLog {

  private final Provider<LogFileEntry> logFileProvider;

  @Inject
public LogFileTransactionLog(Provider<LogFileEntry> logFileProvider) {
this.logFileProvider = logFileProvider;
} public void logChargeResult(ChargeResult result) {
LogFileEntry summaryEntry = logFileProvider.get();
summaryEntry.setText("Charge " + (result.wasSuccessful() ? "success" : "failure"));
summaryEntry.save(); if (!result.wasSuccessful()) {
LogFileEntry detailEntry = logFileProvider.get();
detailEntry.setText("Failure result: " + result);
detailEntry.save();
}
}

当你需要延迟加载一些重要对象,特别是它并不总是需要加载时,使用Provider

public class DatabaseTransactionLog implements TransactionLog {

  private final Provider<Connection> connectionProvider;

  @Inject
public DatabaseTransactionLog(Provider<Connection> connectionProvider) {
this.connectionProvider = connectionProvider;
} public void logChargeResult(ChargeResult result) {
/* only write failed charges to the database */
if (!result.wasSuccessful()) {
Connection connection = connectionProvider.get();
}
}

如果你不希望对象的生命周期混杂,比如一个Singleton的Logger和一个RequestScope的User在一起使用就可能会出现问题。

@Singleton
public class ConsoleTransactionLog implements TransactionLog { private final AtomicInteger failureCount = new AtomicInteger();
private final Provider<User> userProvider; @Inject
public ConsoleTransactionLog(Provider<User> userProvider) {
this.userProvider = userProvider;
} public void logConnectException(UnreachableException e) {
failureCount.incrementAndGet();
User user = userProvider.get();
System.out.println("Connection failed for " + user + ": " + e.getMessage());
System.out.println("Failure count: " + failureCount.incrementAndGet());
}

最佳实践

1.尽量注入不可变对象

如果可以,我们使用构造方法来注入不可变的对象,它们简单,易于分享,可以被组合。

public class RealPaymentService implements PaymentService { 

   private final PaymentQueue paymentQueue;
private final Notifier notifier; @Inject
RealPaymentRequestService(
PaymentQueue paymentQueue,
Notifier notifier) {
this.paymentQueue = paymentQueue;
this.notifier = notifier;
} ...

2.仅注入直接依赖的对象

避免注入一个对象,仅仅是为了使用它获得另一个对象。

public class ShowBudgets {
private final Account account; @Inject
ShowBudgets(Customer customer) {
account = customer.getPurchasingAccount();
}

更好的方法是,在Module中使用@Provides 通过Customer创建一个Account对象

public class CustomersModule extends AbstractModule {
@Override public void configure() {
...
} @Provides
Account providePurchasingAccount(Customer customer) {
return customer.getPurchasingAccount();
}

这样,需要注入的地方代码会更简单

public class ShowBudgets {
private final Account account; @Inject
ShowBudgets(Account account) {
this.account = account;
}

3.解决循环依赖

下面这样的就是循环依赖,Store -> Boss -> Clerk -> Store ...

public class Store {
private final Boss boss;
//... @Inject public Store(Boss boss) {
this.boss = boss;
//...
}
public void incomingCustomer(Customer customer) {...}
public Customer getNextCustomer() {...}
} public class Boss {
private final Clerk clerk;
@Inject public Boss(Clerk clerk) {
this.clerk = clerk;
}
} public class Clerk {
private final Store shop;
@Inject Clerk(Store shop) {
this.shop = shop;
} void doSale() {
Customer sucker = shop.getNextCustomer();
//...
}
}

文档里有解决的办法。https://github.com/google/guice/wiki/CyclicDependencies

4.避免在Module写入具体的逻辑

public class FooModule {
private final String fooServer; public FooModule() {
this(null);
} public FooModule(@Nullable String fooServer) {
this.fooServer = fooServer;
} @Override protected void configure() {
if (fooServer != null) {
bind(String.class).annotatedWith(named("fooServer")).toInstance(fooServer);
bind(FooService.class).to(RemoteFooService.class);
} else {
bind(FooService.class).to(InMemoryFooService.class);
}
}
}

解决方法是将FooModule分成RemoteFooModule 和 InMemoryFooModule。

Google Guice学习的更多相关文章

  1. Guice学习(一)

    Guice学习(一) Guice是Google开发的一个轻量级依赖注入框架(IOC).Guice非常小而且快,功能类似与Spring,但效率上网上文档显示是它的100倍,而且还提供对Servlet,A ...

  2. jdbc框架 commons-dbutils&plus;google guice&plus;servlet 实现一个例子

    最近闲着无聊,于是看了一下jdbc框架 commons-dbutils与注入google guice. 我就简单的封装了一下代码,效率还是可以的.... jdbc+google guice+servl ...

  3. &lbrack;Tensorflow实战Google深度学习框架&rsqb;笔记4

    本系列为Tensorflow实战Google深度学习框架知识笔记,仅为博主看书过程中觉得较为重要的知识点,简单摘要下来,内容较为零散,请见谅. 2017-11-06 [第五章] MNIST数字识别问题 ...

  4. Reading &vert; 《TensorFlow:实战Google深度学习框架》

    目录 三.TensorFlow入门 1. TensorFlow计算模型--计算图 I. 计算图的概念 II. 计算图的使用 2.TensorFlow数据类型--张量 I. 张量的概念 II. 张量的使 ...

  5. 依赖注入框架Google Guice 对象图

    GettingStarted · google/guice Wiki https://github.com/google/guice/wiki/GettingStarted sameb edited ...

  6. 1 如何使用pb文件保存和恢复模型进行迁移学习(学习Tensorflow 实战google深度学习框架)

    学习过程是Tensorflow 实战google深度学习框架一书的第六章的迁移学习环节. 具体见我提出的问题:https://www.tensorflowers.cn/t/5314 参考https:/ ...

  7. Google Guice 之绑定1

    绑定和依赖注入区别 绑定,使用时 需要通过 injector 显示获取 依赖注入,只需要显示获取主类,他的依赖是通过@Injector 和 绑定关系 隐式注入的 http://blog.csdn.ne ...

  8. google guice

    1 google guice是什么 google guice是一个轻量的DI容器. 2 guice和spring对比 spring的配置放在xm文件中,guice的配置放在Module中. guice ...

  9. 【书评】【不推荐】《TensorFlow:实战Google深度学习框架》(第2版)

    参考书 <TensorFlow:实战Google深度学习框架>(第2版) 这本书我老老实实从头到尾看了一遍(实际上是看到第9章,刚看完,后面的实在看不下去了,但还是会坚持看的),所有的代码 ...

随机推荐

  1. Filter之——GZIP全站压缩

    GZIP压缩:将压缩后的文本文件,发送给浏览器,减少流量. 一.进行gzip压缩条件: 1.请求头:Accept-Encoding : gzip  告诉服务器,该浏览器支持gzip压缩. 2.响应头: ...

  2. C&plus;&plus; 输入输出流 总结

    1.ostream & operator<<(int); 重载了<<符号,用于对int类型的输出.为什么要返回ostream的引用呢? 例如: cout<< ...

  3. QJ系列笔记

    1.求int型数据在内存中存储时1的个数输入一个int型数据,计算出该int型数据在内存中存储时1的个数. #include<stdio.h> void main() { ; int yu ...

  4. SDUT 2860-生日Party(BFS)

    生日Party Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描写叙述 Sherlock的生日即将来临,Sherlock打算邀请几个好 ...

  5. &lbrack;Usaco2008 Nov&rsqb;Guarding the Farm 保卫牧场&lbrack;DFS&rsqb;

    Description The farm has many hills upon which Farmer John would like to place guards to ensure the ...

  6. Java只给汉字转URLEncoder

    public static String encode(String str, String charset) throws UnsupportedEncodingException { Patter ...

  7. MySQL中使用Like模糊查询太慢

    问题:明明建立了索引,为何Like模糊查询速度还是特别慢? Like是否使用索引? 1.like %keyword    索引失效,使用全表扫描.但可以通过翻转函数+like前模糊查询+建立翻转函数索 ...

  8. GaugeControl 之 DigitalGauge

    https://documentation.devexpress.com/#WindowsForms/clsDevExpressXtraGaugesWinGaugesDigitalDigitalGau ...

  9. 2015&sol;11&sol;7用Python写游戏,pygame入门&lpar;7&rpar;:碰撞检测

    我们已经完成了飞机大战的大部分东西,但是游戏还是没有办法正式开玩,因为子弹并不能打掉飞机.只有完成了这一个工作,游戏才算基本成型. 今天的内容就非常简单了,就是做到这个碰撞检测,以及控制好子弹和飞机的 ...

  10. HDU 1599 find the mincost route (最短路 floyd)

    题目链接 Problem Description 杭州有N个景区,景区之间有一些双向的路来连接,现在8600想找一条旅游路线,这个路线从A点出发并且最后回到A点,假设经过的路线为V1,V2,....V ...