guice30.docx
《guice30.docx》由会员分享,可在线阅读,更多相关《guice30.docx(34页珍藏版)》请在冰豆网上搜索。
guice30
一.概述
Guice是一个轻量级的DI框架。
本文对Guice的基本用法作以介绍。
本文的所有例子基于Guice3.0
本文的很多代码来源于Guice主页:
考虑到是入门介绍,本文中并未涉及到AOP相关内容,如有需要还请参考上面链接。
二.举例说明Guice的用法
Guice本身只是一个轻量级的DI框架,首先我们通过一个例子来看看怎么使用Guice。
首先有一个需要被实现的接口:
[java]viewplaincopyprint?
1.public interface BillingService {
2.
3. /**
4. * Attempts to charge the order to the credit card. Both successful and
5. * failed transactions will be recorded.
6. *
7. * @return a receipt of the transaction. If the charge was successful, the
8. * receipt will be successful. Otherwise, the receipt will contain a
9. * decline note describing why the charge failed.
10. */
11. Receipt chargeOrder(PizzaOrder order, CreditCard creditCard);
12.}
publicinterfaceBillingService{
/**
*Attemptstochargetheordertothecreditcard.Bothsuccessfuland
*failedtransactionswillberecorded.
*
*@returnareceiptofthetransaction.Ifthechargewassuccessful,the
*receiptwillbesuccessful.Otherwise,thereceiptwillcontaina
*declinenotedescribingwhythechargefailed.
*/
ReceiptchargeOrder(PizzaOrderorder,CreditCardcreditCard);
}
然后,有一个实现该接口的实现类:
[java]viewplaincopyprint?
1.class RealBillingService implements BillingService {
2. private final CreditCardProcessor processor;
3. private final TransactionLog transactionLog;
4.
5. @Inject
6. RealBillingService(CreditCardProcessor processor,
7. TransactionLog transactionLog) {
8. this.processor = processor;
9. this.transactionLog = transactionLog;
10. }
11.
12. @Override
13. public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {
14.
15. }
16.}
classRealBillingServiceimplementsBillingService{
privatefinalCreditCardProcessorprocessor;
privatefinalTransactionLogtransactionLog;
@Inject
RealBillingService(CreditCardProcessorprocessor,
TransactionLogtransactionLog){
this.processor=processor;
this.transactionLog=transactionLog;
}
@Override
publicReceiptchargeOrder(PizzaOrderorder,CreditCardcreditCard){
}
}
现在接口有了,实现类也有了,接下来就是如何将接口和实现类关联的问题了,在Guice中需要定义Module来进行关联
[java]viewplaincopyprint?
1.public class BillingModule extends AbstractModule {
2. @Override
3. protected void configure() {
4.
5. /*
6. * This tells Guice that whenever it sees a dependency on a
7. * TransactionLog, it should satisfy the dependency using a
8. * DatabaseTransactionLog.
9. */
10. bind(TransactionLog.class).to(DatabaseTransactionLog.class);
11.
12. /*
13. * Similarly, this binding tells Guice that when CreditCardProcessor is
14. * used in a dependency, that should be satisfied with a
15. * PaypalCreditCardProcessor.
16. */
17. bind(CreditCardProcessor.class).to(PaypalCreditCardProcessor.class);
18. }
19.}
publicclassBillingModuleextendsAbstractModule{
@Override
protectedvoidconfigure(){
/*
*ThistellsGuicethatwheneveritseesadependencyona
*TransactionLog,itshouldsatisfythedependencyusinga
*DatabaseTransactionLog.
*/
bind(TransactionLog.class).to(DatabaseTransactionLog.class);
/*
*Similarly,thisbindingtellsGuicethatwhenCreditCardProcessoris
*usedinadependency,thatshouldbesatisfiedwitha
*PaypalCreditCardProcessor.
*/
bind(CreditCardProcessor.class).to(PaypalCreditCardProcessor.class);
}
}
好了,现在万事俱备,就让我们一起看看怎么使用Guice进行依赖注入吧:
[java]viewplaincopyprint?
1.public static void main(String[] args) {
2.
3. /*
4. * Guice.createInjector() takes your Modules, and returns a new Injector
5. * instance. Most applications will call this method exactly once, in their
6. * main() method.
7. */
8. Injector injector = Guice.createInjector(new BillingModule());
9.
10. /*
11. * Now that we've got the injector, we can build objects.
12. */
13. RealBillingService billingService = injector.getInstance(RealBillingService.class);
14.}
publicstaticvoidmain(String[]args){
/*
*Guice.createInjector()takesyourModules,andreturnsanewInjector
*instance.Mostapplicationswillcallthismethodexactlyonce,intheir
*main()method.
*/
Injectorinjector=Guice.createInjector(newBillingModule());
/*
*Nowthatwe'vegottheinjector,wecanbuildobjects.
*/
RealBillingServicebillingService=injector.getInstance(RealBillingService.class);
}
以上就是使用Guice的一个完整的例子,很简单吧,不需要繁琐的配置,只需要定义一个Module来表述接口和实现类,以及父类和子类之间的关联关系的绑定。
本文不对比guice和spring,只是单纯介绍Guice的用法。
三.绑定方式的介绍
从上面我们可以看出,其实对于Guice而言,程序员所要做的,只是创建一个代表关联关系的Module,然后使用这个Module即可得到对应关联的对象。
因此,主要的问题其实就是在如何关联实现类和接口(子类和父类)。
1.在自定义的Module类中进行绑定
1.1在configure方法中绑定
1.1.1链式绑定
链式绑定是最简单,最直接,也是使用最多的绑定方式。
[java]viewplaincopyprint?
1.protected void configure() {
2. bind(TransactionLog.class).to(DatabaseTransactionLog.class);
3.}
protectedvoidconfigure(){
bind(TransactionLog.class).to(DatabaseTransactionLog.class);
}
就是直接把一种类型的class对象绑定到另外一种类型的class对象,这样,当外界获取TransactionLog时,其实返回的就是一个DatabaseTransactionLog对象。
当然,链式绑定也可以串起来,如:
[java]viewplaincopyprint?
1.protected void configure() {
2. bind(TransactionLog.class).to(DatabaseTransactionLog.class);
3. bind(DatabaseTransactionLog.class).to(MySqlDatabaseTransactionLog.class);
4.}
protectedvoidconfigure(){
bind(TransactionLog.class).to(DatabaseTransactionLog.class);
bind(DatabaseTransactionLog.class).to(MySqlDatabaseTransactionLog.class);
}
这样,当外界请求TransactionLog时,其实返回的就会是一个MySqlDatabaseTransactionLog对象。
1.1.2注解(Annotations)绑定
链式绑定针对于同样的类型都绑定到同一种目标类型时,非常好用,但是对于一个接口有多种实现的时候,链式绑定就不好区分该用哪种实现了。
可以把Annotations绑定方式看作是链式绑定的一种扩展,专门用来解决这种同一个接口有多种实现的问题。
Annotations绑定又可以分为两种,一种是需要自己写Annotations,另外一种则简化了一些。
1.1.2.1自己写Annotations的方式
首先,写一个注解
[java]viewplaincopyprint?
1.import com.google.inject.BindingAnnotation;
2.import java.lang.annotation.Target;
3.import java.lang.annotation.Retention;
4.import static java.lang.annotation.RetentionPolicy.RUNTIME;
5.import static java.lang.annotation.ElementType.PARAMETER;
6.import static java.lang.annotation.ElementType.FIELD;
7.import static java.lang.annotation.ElementType.METHOD;
8.
9.@BindingAnnotation
10.@Target({ FIELD, PARAMETER, METHOD })
11.@Retention(RUNTIME)
12.public @interface PayPal {
13.}
importcom.google.inject.BindingAnnotation;
importjava.lang.annotation.Target;
importjava.lang.annotation.Retention;
importstaticjava.lang.annotation.RetentionPolicy.RUNTIME;
importstaticjava.lang.annotation.ElementType.PARAMETER;
importstaticjava.lang.annotation.ElementType.FIELD;
importstaticjava.lang.annotation.ElementType.METHOD;
@BindingAnnotation
@Target({FIELD,PARAMETER,METHOD})
@Retention(RUNTIME)
public@interfacePayPal{
}
然后,使用这个注解去修饰目标字段或参数,如:
[java]viewplaincopyprint?
1.public class RealBillingService implements BillingService {
2.
3. @Inject
4. public RealBillingService(@PayPal CreditCardProcessor processor,
5. TransactionLog transactionLog) {
6. }
7.}
publicclassRealBillingServiceimplementsBillingService{
@Inject
publicRealBillingService(@PayPalCreditCardProcessorprocessor,
TransactionLogtransactionLog){
}
}
或
[java]viewplaincopyprint?
1.public class RealBillingService implements BillingService {
2. @Inject
3. @Www
4. private CreditCardProcessor processor;
5.}
publicclassRealBillingServiceimplementsBillingService{
@Inject
@Www
privateCreditCardProcessorprocessor;
}
最后,在我们进行链式绑定时,就可以区分一个接口的不同实现了,如:
[java]viewplaincopyprint?
1.bind(CreditCardProcessor.class)
2. .annotatedWith(PayPal.class)
3. .to(PayPalCreditCardProcessor.class);
bind(CreditCardProcessor.class)
.annotatedWith(PayPal.class)
.to(PayPalCreditCardProcessor.class);
这样,被Annotations PayPal?
修饰的CreditCardProcessor就会被绑定到目标实现类PayPalCreditCardProcessor。
如果有其他的实现类,则可把用不同Annotations修饰的CreditCardProcessor绑定到不同的实现类
1.1.2.2使用@Named的方式
使用@Named的方式和上面自己写Annotation的方式很类似,只不过做了相应的简化,不再需要自己去写Annotation了。
[java]viewplaincopyprint?
1.public class RealBillingService implements BillingService {
2.
3. @Inject
4. public RealBillingService(@Named("Checkout") CreditCardProcessor processor,
5. TransactionLog transactionLog) {
6. }
7.}
publicclassRealBillingServiceimplementsBillingService{
@Inject
publicRealBillingService(@Named("Checkout")CreditCardProcessorprocessor,
TransactionLogtransactionLog){
}
}
直接使用@Named修饰要注入的目标,并起个名字,下面就可以把用这个名字的注解修饰的接口绑定到目标实现类了
[java]viewplaincopyprint?
1.bind(CreditCardProcessor.class)
2. .annotatedWith(Names.named("Checkout"))
3. .to(CheckoutCreditCardProcessor.class);
bind(CreditCardProcessor.class)
.annotatedWith(Names.named("Checkout"))
.to(CheckoutCreditCardProcessor.class);
1.1.3实例绑定
上面介绍的链式绑定是把接口的class对象绑定到实现类的class对象,而实例绑定则可以看作是链式绑定的一种特例,它直接把一个实例对象绑定到它的class对象上。
[java]viewplaincopyprint?
1.bind(String.class)
2. .annotatedWith(Names.named("JDBC URL"))
3. .toInstance("jdbc:
mysql:
//localhost/pizza");
4.bind(Integer.class)
5. .annotatedWith(Names.named("login timeout seconds"))
6. .toInstance(10);
bind(String.class)
.annotatedWith(Names.named("JDBCURL"))
.toInstance("jdbc:
mysql:
//localhost/pizza");
bind(Integer.class)
.annotatedWith(Names.named("logintimeoutseconds"))
.toInstance(10);
需要注意的是,实例绑定要求对象不能包含对自己的引用。
并且,尽量不要对那种创建实例比较复杂的类使用