Guice发明目的为了1.分离依赖 2.方便测试(插入fake依赖),当然这也正是DI提出的目标。
bind主要用于勾画类的关系(如继承)
与Spring比较
@Named -> @Inject
没继承 -> @Inject 不需要@Named
@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();
}
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常数的实例,但复合类型不用此法而是用@Providesbind(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