Guice基本用法

本文适合对依赖注入有相对了解的读者,文章中对于部分名词未作详细解释。对于没有恰当的中文与之对应的英文内容,遂未翻译

Guice简介

Guice 简介,本文中的内容也是参考该文档完成,如有不一致,以该文为准。

快速上手

作为示例,我们使用 BillingService,它依赖于 CreditCardProcessorTransactionLog 两个接口。接下来我们看看如何使用Guice:


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

我们将会把 PaypalCreditCardProcessor 和 DatabaseTransactionLog 注入BillingService。
Guice 使用 bindings 将类型和实现对应起来。module 是特定的 bindings 的集合。


public class BillingModule extends AbstractModule {@Override protected void configure() {/***这是告诉Guice,当遇到一个对象依赖于TransactionLog时,*将DatabaseTransactionLog注入*/bind(TransactionLog.class).to(DatabaseTransactionLog.class);/**同上*/bind(CreditCardProcessor.class).to(PaypalCreditCardProcessor.class);}
}

现在可以编写main方法了:


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

injector 的职责是确定系统中需要构造哪些对象,解析依赖,装配对象(将被依赖的对象注入依赖的对象)。那么如何指定依赖的解析方式,答案是使用 bindings 配置你的 injector

创建bindings

继承 AbstractModule 重写 configure 方法。在该方法中调用 bind() 便创建了一个binding。完成module之后,调用 Guice.createInjector(),将module作为参数传入,便可获得一个injector对象。

Linked Bindings

Linked bindings 将一个实现绑定到一个类型。
下面例子将 DatabaseTransactionLog 绑定到接口 TransactionLog


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

绑定之后,当你调用 injector.getInstance(TransactionLog.class) 或当injector遇到一个对象依赖与 TransactionLog,它便会使用 DatabaseTransactionLog。链接可以建立于接口和其实现类,或者子类和父类之间。Linked bindings 可以链式使用。


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

在这种情况下,当一个对象依赖于 TransactionLog,injector将会返回一个 MySqlDatabaseTransactionLog.

BindingAnnotations

Binding Annotations

有时候,你想要给特定的类型绑定多个实现。例如,你想给 CreditCardProcessor同时绑定 PaypalCreditCardProcessorCheckoutCreditCardProcessor 两个实现. Guice 通过binding annotation满足上述需求。注解和类型共同确定了一个唯一的binding。 在这儿,注解和类型构成了Key

首先我们定义一个注解:


import com.google.inject.BindingAnnotation;
import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME)
public @interface PayPal {}

然后使用我们定义的注解标示需要注入的类型。


public class RealBillingService implements BillingService {@Injectpublic RealBillingService(@PayPal CreditCardProcessor processor,TransactionLog transactionLog) {...}

最后我们还需要创建相应的binding。

bind(CreditCardProcessor.class).annotatedWith(PayPal.class).to(PayPalCreditCardProcessor.class);

@Named

也并不是必须创建自己的注解,Guice提供了一个内建的注解@Named。用法如下:

public class RealBillingService implements BillingService {@Injectpublic RealBillingService(@Named("Checkout") CreditCardProcessor processor,TransactionLog transactionLog) {...}bind(CreditCardProcessor.class).annotatedWith(Names.named("Checkout")).to(CheckoutCreditCardProcessor.class);

因为编译器不能对String类型进行检查,所以不建议使用@Named

Instance Bindings

你可以将一个特定类型的实例绑定到该类型。

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

尽量避免给创建起来比较复杂的对象使用 .toInstance 方法,那样会导致应用启动比较慢。可以使用 @Provides 代替该方法。

Provides Methods

当你需要编写创建对象的代码,使用 @Provides 方法。该方法只能定义在module中。并且需要使用 @Provides 修饰。他的返回值是一个对象。当Injector需要某个类型的实例时,便会调用相应的@Provides 方法。

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

如果 @Provides 方法有binding annotation ,比如@Paypal 或者 @Named("Checkout"),Guice 也是可以的。所有的被依赖对象以参数形式传入该方法即可。

@Provides @PayPalCreditCardProcessor providePayPalCreditCardProcessor(@Named("PayPal API key") String apiKey) {PayPalCreditCardProcessor processor = new PayPalCreditCardProcessor();processor.setApiKey(apiKey);return processor;}

需要注意的是, Guice不允许 @Provides 方法抛出异常。

Provider Bindings

@Provides 方法比较复杂时,你也许会考虑将该方法转移到一个单独的类中。Provider类继承Guice的 Provider 接口。 Provider 接口定义如下:

public interface Provider<T> {T get();
}

我们的Provider实现类有自己的依赖,所有的依赖是通过被@Inject 修饰的构造函数接收的。

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

绑定

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

Untargeted Bindings

一些情况下,你需要创建bindings,但是却不能指定具体的实现。这个对于被@ImplementedBy 或者 @ProvidedBy 修饰的具体类或者类型特别有用。An untargetted binding informs the injector about a type, so it may prepare dependencies eagerly. Untargetted bindings have no to clause, like so:

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

当指定binding annotation时,必须加上绑定的目标。

bind(MyConcreteClass.class).annotatedWith(Names.named("foo")).to(MyConcreteClass.class);
bind(AnotherConcreteClass.class).annotatedWith(Names.named("foo")).to(AnotherConcreteClass.class).in(Singleton.class);

Constructor Bindings

有时候, 我们需要绑定一个类型到任意的的构造函数。以下情况会有这种需求:@Inject 注解无法被应用到目标构造函数。或者该类型是一个第三方类。或者该类型中有多个构造函数参与依赖注入。
针对这个,Guice 有 @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);}}
}

JustInTimeBindings

Just-in-time Bindings

当Injector需要一个类型的实例的时候,它需要一个binding。 如果这个binding在一个module中被创建,那么这个binding是显式binding,此时injector在每次需要该类型实例时,都使用该实例。但是如果Injector需要一个类型的实例,但是这个类型并没有对应的显式binding。此时injector会尝试创建一个Just-in-time binding。也叫JIT binding或者隐式binding。

合格的构造函数

Guice会使用具体类型的可注入构造函数创建binding。可注入构造函数需要是非private,无参数的或者该构造函数被 @Inject 修饰。

public class PayPalCreditCardProcessor implements CreditCardProcessor {private final String apiKey;@Injectpublic PayPalCreditCardProcessor(@Named("PayPal API key") String apiKey) {this.apiKey = apiKey;}

@ImplementedBy

告诉injector,该类型的默认实现类。

@ImplementedBy(PayPalCreditCardProcessor.class)
public interface CreditCardProcessor {ChargeResult charge(String amount, CreditCard creditCard)throws UnreachableException;
}

上述代码和一下代码是等价的:

    bind(CreditCardProcessor.class).to(PayPalCreditCardProcessor.class);

如果某个类型同时使用 bind()@ImplementedBybind() 会生效。

@ProvidedBy

告诉Injector,产生该类型的Provider类

@ProvidedBy(DatabaseTransactionLogProvider.class)
public interface TransactionLog {void logConnectException(UnreachableException e);void logChargeResult(ChargeResult result);
}

上述代码等价于:

bind(TransactionLog.class).toProvider(DatabaseTransactionLogProvider.class);

如果同时使用 @ProvidedBybind() , bind() 会生效。

Scopes

默认情况下,Guice 每次都会返回一个新创建的对象。不过这也是可以配置的,以便于我们重用实例。

配置Scopes

Guice 使用注解标示Scope。例如:

@Singleton
public class InMemoryTransactionLog implements TransactionLog {/* everything here should be threadsafe! */
}@Provides @Singleton
TransactionLog provideTransactionLog() {...
}

上例中,@Singleton 标示该类型只能有一个实例。并且是线程安全的。

Scope也可以通过代码来配置:

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

如果某个类型已经被注解标注了scope,同时还使用bind() 方法配置了scope,那么后者生效。如果一个类型已经被注解配置了scope而你不想那样,你可以使用 bind() 覆盖。

预加载的单例

bind(TransactionLog.class).to(InMemoryTransactionLog.class).asEagerSingleton();

Injections

构造函数注入

这种情况下,需要用 @Inject 标注构造函数。构造函数同时需要将所依赖的类型作为其参数。通常情况下,都是将传入的参数复制给类的final成员。

public class RealBillingService implements BillingService {private final CreditCardProcessor processorProvider;private final TransactionLog transactionLogProvider;@Injectpublic RealBillingService(CreditCardProcessor processorProvider,TransactionLog transactionLogProvider) {this.processorProvider = processorProvider;this.transactionLogProvider = transactionLogProvider;}

如果没有 @Inject 标注的构造函数,Guice 会使用共有的午餐构造函数(如果存在)。

方法注入

这种情况下,需要使用@Inject 标注方法,该方法的参数为需要注入的类型。

public class PayPalCreditCardProcessor implements CreditCardProcessor {private static final String DEFAULT_API_KEY = "development-use-only";private String apiKey = DEFAULT_API_KEY;@Injectpublic void setApiKey(@Named("PayPal API key") String apiKey) {this.apiKey = apiKey;}

字段注入

这种情况下,需要使用 @Inject 标注字段。

public class DatabaseTransactionLogProvider implements Provider<TransactionLog> {@Inject Connection connection;public TransactionLog get() {return new DatabaseTransactionLog(connection);}
}

可选的注入

有时候,我们的依赖项不是必须的,如果系统中存在依赖项则注入,如果不存在,也不强制要求注入。这种情况在方法注入和字段注入中都是适用的。 启用可选注入,只需要使用 @Inejct(optional=true) 标注字段或方法即可。

public class PayPalCreditCardProcessor implements CreditCardProcessor {private static final String SANDBOX_API_KEY = "development-use-only";private String apiKey = SANDBOX_API_KEY;@Inject(optional=true)public void setApiKey(@Named("PayPal API key") String apiKey) {this.apiKey = apiKey;}

不过,如果混用可选注入和Just-in-time bindings,可能会产生奇怪的接口。例如:

@Inject(optional=true) Date launchDate;

上面代码中的date总是会被成功注入即使没有为他创建对应的显示binding,因为它有无参构造函数,Guice会为他创建Just-in-time bindings。

On-demand注入

方法和字段注入可以用来初始化一个现存的实例。我们可以使用Injector.injectMember()API:

public static void main(String[] args) {Injector injector = Guice.createInjector(...);CreditCardProcessor creditCardProcessor = new PayPalCreditCardProcessor();injector.injectMembers(creditCardProcessor);

AOP

Guice 支持方法拦截器。这里直接看一个实例:
假如我们需要禁止在周末调用特定方法

为了标注我们在周末禁止调用的方法,我们定义一个注解类型:

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD)
@interface NotOnWeekends {}

然后使用该注解标注我们方法

public class RealBillingService implements BillingService {@NotOnWeekendspublic Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {...}
}

现在我们定义我们的拦截器,我们的拦截器需要实现org.aopalliance.intercept.MethodInterceptor 接口。

public class WeekendBlocker implements MethodInterceptor {public Object invoke(MethodInvocation invocation) throws Throwable {Calendar today = new GregorianCalendar();if (today.getDisplayName(DAY_OF_WEEK, LONG, ENGLISH).startsWith("S")) {throw new IllegalStateException(invocation.getMethod().getName() + " not allowed on weekends!");}return invocation.proceed();}
}

然后配置我们的拦截器

public class NotOnWeekendsModule extends AbstractModule {protected void configure() {/**在这里,我们匹配所有的类,但是只匹配类中有NotOnWeekends的方法*/bindInterceptor(Matchers.any(), Matchers.annotatedWith(NotOnWeekends.class), new WeekendBlocker());}
}

所有工作就完成了。

注入拦截器

如果需要注入拦截器,使用 `requestInjection` API

public class NotOnWeekendsModule extends AbstractModule {protected void configure() {WeekendBlocker weekendBlocker = new WeekendBlocker();requestInjection(weekendBlocker);bindInterceptor(Matchers.any(), Matchers.annotatedWith(NotOnWeekends.class), weekendBlocker);}
}

另外一种方式是使用 `Binder.getProvider`,将依赖的内容传入拦截器的构造函数。

public class NotOnWeekendsModule extends AbstractModule {protected void configure() {bindInterceptor(any(),annotatedWith(NotOnWeekends.class),new WeekendBlocker(getProvider(Calendar.class)));}
}

END

本人有意进一步阅读Guice源码,但是个人能力有限,如有感兴趣的读者,希望可以一起研究。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/499704.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Guice之Servlet基础

如果读者对于Guice没有大体的了解&#xff0c;可以参考本人的另一篇Guice基础文章 Guice 提供了一个完整的体系使得我们在web应用中也可以使用它作为依赖注入的工具. 为什么使用 Guice : 使用Guice的好处: 构造函数注入类型安全的, 方便的配置方式(只需要在web.xml中进行很…

矩阵 I : 矩阵基础

学习机器学习, 基础的线性代数知识是必备的基础功, 对于线性代数的探索, 矩阵是线性代数的主要研究对象. 今天我们就开始学习一下矩阵的基础知识. 这是本人关于线性代数矩阵的第一篇分享. 章节目录 矩阵及其基本运算 1.1 矩阵定义 1.2 矩阵基本运算(,-,*) 1.3 转置矩阵 1…

矩阵 II : 线性组的线性相关性

学习机器学习, 基础的线性代数知识是必备的基础功, 对于线性代数的探索, 向量组也是线性代数的重要基础. 今天我们就开始学习一下线性代数中重要的向量组知识. 这是本人关于线性组的线性相关性的学习分享. 章节目录 相关性基本概念 1.1 相性相关和线性无关 1.2 相性相关性的…

汇编语言笔记(一):基础

章节目录 简单程序 使用段简单字符串处理程序使用 bx, si, di, bp 寄存器寻址寻址方法指明数据长度div指令 作者能力有限, 如果您在阅读过程中发现任何错误, 还请您务必联系本人,指出错误, 避免后来读者再学习错误的知识.谢谢! 本文中所有程序均在DOSBox下使用MASM, LINK编译运…

汇编学习笔记(二):转移指令

章节目录 转移指令原理 jmp 指令 jcxz 指令 loop 指令 ret 和 retf 指令 call 指令 callret 作者能力有限, 如果您在阅读过程中发现任何错误, 还请您务必联系本人,指出错误, 避免后来读者再学习错误的知识.谢谢! 本文中所有程序均在DOSBox下使用MASM, LINK编译运行 转移指令…

汇编语言笔记(三): 标志寄存器

章节目录 简介 ZF 标志寄存器PF 标志寄存器SF 标志寄存器CF 标志寄存器OF 标志寄存器几条相关指令DF 标志寄存器PUSHF and POPF 标志寄存器 作者能力有限, 如果您在阅读过程中发现任何错误, 还请您务必联系本人,指出错误, 避免后来读者再学习错误的知识.谢谢! 简介 8086 CPU…

汇编语言笔记(四):内中断

汇编语言笔记:内中断 章节目录 概念 中断过程示例: 0 号中断处理 作者能力有限, 如果您在阅读过程中发现任何错误, 还请您务必联系本人,指出错误, 避免后来读者再学习错误的知识.谢谢! 概念 中断信息: 任何一个通用 CPU 都具备一种能力, 可以在执行完当前正在执行的指令之…

sync.Map 源码学习

golang 线程安全的 Map 作者水平有限,而并发博大精深. 如文章中有任何错误, 希望读者不吝指出.谢谢! 章节目录 Map 基本类型定义StoreLoadDeleteRange Map 基本类型定义## Map 这里我们先看一下 Map 都涉及到那些类型. type Map struct {// 互斥锁. 用于互斥的读写 dirty.…

ASN.1 学习

ASN.1 章节目录 简介常用数据类型 2.1 常见的简单类型 2.2 结构类型Basic Encoding RulesDistinguished Encoding Rules编码示例 5.1 BIT STRING 5.2 IA5String 5.3 INTEGER 5.4 NULL 5.5 OCTET STRING 5.6 UTCTime 5.6 OBJECT IDENTIFIER编码 Name (X.501 type) 参考 http://…

证书体系: PFX 文件格式解析

原文同时发布于本人个人博客&#xff1a; https://kutank.com/blog/cert-pfx/ 章节目录 PFX 简介PFX 格式解析 2.1 最外层结构 2.2 AuthenticatedSafe 结构 参考 https://tools.ietf.org/html/rfc7292. PFX 简介## 以下引用自维基百科 在密码学中&#xff0c;PKCS #12 定义了…

C10K 非阻塞 Web 服务器

本文由作为 Going Concurrency in Go 的作者 Nathan Kozyra 撰写, 解决了互联网上最著名,最受尊敬的挑战之一, 并试图通过核心 Go 包来解决它. 原文地址: https://hub.packtpub.com/c10k-non-blocking-web-server-go/ 我们已经构建了一些可用的应用程序,并且可以在日常使用的真…

MD5 算法描述及实现

MD5 算法的原理及实现 章节目录 简介算法描述 实现 作者能力有限, 如果您在阅读过程中发现任何错误, 还请您务必联系本人,指出错误, 避免后来读者再学习错误的知识.谢谢! 简介## Wiki对其的描述: MD5消息摘要算法&#xff08;英语&#xff1a;MD5 Message-Digest Algorithm&…

SHA 算法描述与实现

SHA 算法的原理及实现 章节目录 简介算法描述 2.1 数据准备 2.1.1 数据填充 2.1.2 数据分块 2.1.3 设置初始 Hash 值 2.2 Hash 计算 2.2.1 SHA-1 2.2.2 SHA-256 2.2.3 SHA-512实现 作者能力有限, 如果您在阅读过程中发现任何错误, 还请您务必联系本人,指出错误, 避免后来读者…

SHA算法描述及实现

SHA 算法的原理及实现 章节目录 简介算法描述 2.1 数据准备 2.1.1 <数据填充 2.1.2 数据分块 2.1.3 设置初始 Hash 值 2.2 Hash 计算 2.2.1 SHA-1 2.2.2 SHA-256 2.2.3 SHA-512实现<b>作者能力有限, 如果您在阅读过程中发现任何错误, 还请您务必联系本人,指出错误, …

CNG 关于 Key 相关的操作

章节目录 简介创建 Key查看系统中的 Key从 Windows Store 导出 key导入 Key 到 Windows Store<b>作者能力有限, 如果您在阅读过程中发现任何错误, 还请您务必联系本人,指出错误, 避免后来读者再学习错误的知识.谢谢!</b> 简介 CNG 全称 Cryptography API: Next G…

Golang 词法分析器浅析

浅析 Go 语言的词法分析器 章节目录 简介TokenScanner例子 作者能力有限, 如果您在阅读过程中发现任何错误, 还请您务必联系本人,指出错误, 避免后来读者再学习错误的知识.谢谢! 简介## 在本文我们将简单的走读 Go 语言的词法分析器实现(go/scanner/scanner.go). 本文基于 G…

如何读懂 C 语言复杂的声明

如何读懂 C 语言复杂的声明 作者能力有限, 如果您在阅读过程中发现任何错误, 还请您务必联系本人,指出错误, 避免后来读者再学习错误的知识.谢谢! 参考<<C专家编程>> 废话 虽说 C 语言相比于其他更高级的语言来讲&#xff0c;有着非常精简的语法结构&#xff0c;对…

C 语言笔记: 链表节点实现技巧--struct的妙用

链表节点实现技巧–struct的妙用 作者能力有限, 如果您在阅读过程中发现任何错误, 还请您务必联系本人,指出错误, 避免后来读者再学习错误的知识.谢谢! 废话 C 语言虽然只提供了非常简单的语法&#xff0c;但是丝毫不影响 C 语言程序员使用 C 来实现很多让人叹为观止的高级功能…

协议簇: Media Access Control(MAC) Frame 解析

Media Access Control(MAC) Frame 解析 前言 千里之行&#xff0c;始于足下。 因为个人从事网络协议开发&#xff0c;一直想深入的学习一下协议族&#xff0c;从这篇开始&#xff0c;我将开始记录分享我学习到的网络协议相关的知识 简介 引用百度百科的描述&#xff1a; 数…

协议簇:Ethernet Address Resolution Protocol (ARP) 解析

简介 前面的文章中&#xff0c;我们介绍了 MAC Frame 的帧格式。我们知道&#xff0c;在每个 Ethernet Frame 中都分别包含一个 48 bit 的源物理地址和目的物理地址. 对于源地址很容易理解&#xff0c;该地址可以直接从硬件上读取. 但是对于一个网络节点&#xff0c;他怎么知道…