Monday, March 6, 2017

Guice简介

Guice也是采用DI标准。Guice比Spring轻量级,启动速度稍快,但是没有Spring完善。
Guice发明目的为了1.分离依赖 2.方便测试(插入fake依赖),当然这也正是DI提出的目标。

bind主要用于勾画类的关系(如继承)

与Spring比较
@Named -> @Inject

没继承 -> @Inject 不需要@Named
@provides -> @Inject 如果提供实例需另外处理。当然还可以加上@Singleton,它可在provides或类名前。

最简单的例子

模拟在商店用信用卡买食品的系统:

public class App
{
    public static void main( String[] args )
    {
        Injector injector = Guice.createInjector(new BillingModule());
        BillingService billingService = injector.getInstance(BillingService.class);
        billingService.chargeOrder("pizza", new CreditCard());
    }
}

public class BillingModule extends AbstractModule {
@Override
protected void configure() {}
}

public class CreditCardProcessor {
String name = "Paypal";
}

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

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

public int chargeOrder(String order, CreditCard creditCard) {
System.out.println("processing purchase");
System.out.println(processor.toString());
System.out.println(transactionLog.toString());
return 0;
}
}

1. 在Module里面的binding不是必须的,如果有类关系(如继承)才需要在这里配置。
2. Guice的Inject一般在构造函数,这样私有成员可以声明final,比较安全。只要用@Inject,     Guice就会自动注入,不需要在CreditCardProcessor加上Named(Spring做法)
3. Guice首先根据Module生成Injector,然后获得启动类再启动服务。
比较一下Spring的启动方式,很相似,区别就在于类关系表现在class还是xml
ApplicationContext context = new ClassPathXmlApplicationContext("com/vtasters/beans.xml");
Video t = (Video) context.getBean("video");

Binding绑定

当Inject一个interface时候,需要在config说明用哪个实现类代替这个interface,这就是binding的作用。
@Inject
BillingService(CreditCardProcessor processor, TransactionLog transactionLog)
CreditCardProcessor此时是Interface非实现类,所以要说明其实现类。

在config中加入bind(CreditCardProcessor.class).to(PaypalCreditCardProcessor.class);

interface CreditCardProcessor {
String getName();
}

public class PaypalCreditCardProcessor implements CreditCardProcessor{
String name = "Paypal";
public String getName(){return name;}
}

另一方法是JustInTimeBindings用@ImplementedBy
@ImplementedBy(PayPalCreditCardProcessor.class)
public interface CreditCardProcessor
这样就不用写bind

BindingAnnotations注释绑定

用注释来提高绑定的代码可读性

第一种方法是@interface法,用@interface创建自定义注释。
粗体部分为新加入。加入@PayPal后目的是提高代码易读性,这样不用去binding就知道CreditCardProcessor绑定到PaypalCreditCardProcessor。当然同时也产生了额外的代码(@interface类)

@BindingAnnotation
@Target({ FIELD, PARAMETER, METHOD })
@Retention(RUNTIME)
public @interface PayPal {}

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

@Inject BillingService(@PayPal CreditCardProcessor processor, TransactionLog transactionLog)

第二种方法是@Named法,内嵌式注释。此法省去@interface创建,但忘记绑定或绑定字符串typo编译器不能发现,所以Guice不推荐

public class DatabaseTransactionLog implements TransactionLog加入实现类

bind(TransactionLog.class).annotatedWith(Names.named("Database")).to(DatabaseTransactionLog.class);

@Inject BillingService(@PayPal CreditCardProcessor processor,
                                      @Named("Database") TransactionLog transactionLog)

InstanceBindings绑定实例

通过注释绑定一个String常数的实例,但复合类型不用此法而是用@Provides
bind(String.class).annotatedWith(Names.named("url")).toInstance("jdbc:mysql://localhost/pizza");
可以用此法绑定更好
bindConstant().annotatedWith(Names.named("dbname")).to("MySQL");


@Provides提供实例

如果不用绑定可以用@Provides,如下例provideStore方法代替了bind(Store.class).to(RegularStore.class)

public class BillingModule extends AbstractModule {
@Provides
Store provideStore() {
RegularStore store = new RegularStore();
store.setId("123");
return store;
}
}

@Inject BillingService(@PayPal CreditCardProcessor processor, @Named("Database") TransactionLog transactionLog, Store store)

Provides也可以与binding一样加注释:
        @Provides
@Named("regular")
Store provideStore()

@Inject BillingService(@PayPal CreditCardProcessor processor, @Named("Database") TransactionLog transactionLog, @Named("regular") Store store)


ProviderBindings提供绑定

如果@Provides模块太大需要独立成一个class可以实现Provider做到,还要加入bind中。
public class RegularEmployeeProvider implements Provider<Employee> {
public Employee get() {
RegularEmployee employee = new RegularEmployee("Sue");
return employee;
}
}

@Inject BillingService(@PayPal CreditCardProcessor processor, @Named("Database") TransactionLog transactionLog, @Named("regular") Store store, Employee employee)

bind(Employee.class).toProvider(RegularEmployeeProvider.class);

另一方法是JustInTimeBindings用@ProvidedBy
@ProvidedBy(RegularEmployeeProvider.class)
public interface Employee
这样就不用写bind

范围(如单例)

表示每次需要实例时,提供同一实例。在application运行期均为单例。
@Provides
@Named("regular")
@Singleton
Store provideStore()

以下两个服务都返回同一个RegularStore
        BillingService billingService = injector.getInstance(BillingService.class);    
        BillingService billingService2 = injector.getInstance(BillingService.class);

上法是Provides法,还可以用binding法和加到RegularStore上面,若冲突,以binding为准。
bind(Store.class).to(RegularStore.class).in(Singleton.class);

@Singleton
public class RegularStore implements Store

非继承类

无继承的类可以用@Provides来提供实例,正如之前提过不用binding
@Provides
@Singleton
Cache provideCache(){
Cache cache = new Cache();
cache.setName("mycache");
return cache;
}
@Inject BillingService(@PayPal CreditCardProcessor processor, @Named("Database") TransactionLog transactionLog, @Named("regular") Store store, Employee employee, Cache cache)


ref:
官方
HeroModule
provider design pattern
HelloGuiceServiceImpl
singleton

No comments:

Post a Comment