Guice
在上一篇博客中, 我們講解了Spring中的IOC示例與實現, 本文着重介紹Guice注入以及與Spring中的差異.
Guice是Google開發的, 一個輕量級的依賴注入框架, 跟Spring最大的區別在於脫離xml配置,
大量使用Annotation來實現注入, 支持屬性, 構造器, setter等多種方式注入對象.
Guice 3.0支持 jdk 1.6, 如果運行報錯ClassNotFoundException: javax.inject.Provider, 則需要導入javax.inject包.
Module容器
Guice中容器即Module, 用於綁定接口 : 實現類, 類似於Spring中的applicationContext.xml.
Module像是一個Map,根據一個Key獲取其Value,清楚明了的邏輯.
以下代碼實現了一個簡單的注入
1 Injector ij = Guice.createInjector(new Module() { 2 @Override 3 public void configure(Binder binder) { 4 binder.bind(TestService.class).to(ServiceImpl.class); 5 } 6 }); 7 ij.getInstance(TestService.class).test();
支持繞過Module, 用默認配置, 直接實例化對象, 不過沒啥意義, 除非要用容器做aop
1 Injector ij2 = Guice.createInjector(); 2 ij2.getInstance(ServiceImpl.class).test();
當然也可以使用注解的方式來聲明接口的實現類, 然后Injector 從接口中獲取對象,
意義也不大, 因為實際業務中, 接口可能在上層包里, 無法直接調用實現類.
1 @ImplementedBy(ServiceImpl.class) 2 public interface TestService { 3 4 void test(); 5 } 6 7 --------------------------------------- 8 9 Injector ij3 = Guice.createInjector(); 10 ij3.getInstance(TestService.class).test();
@Inject屬性注入
1 public class GuiceObjectDemo { 2 3 @Inject 4 private TestService service1; 5 @Inject 6 private TestService service2; 7 8 --------------------------------------- 9 10 GuiceObjectDemo demo = Guice.createInjector().getInstance(GuiceObjectDemo.class); 11 System.out.println(demo.getService()); 12 System.out.println(demo.getService2());
屬性注入的時候, 必須通過Guice.createInjector().getInstance(GuiceObjectDemo.class);來獲取實現類, 如果直接new的話, 會inject失敗, 打印出兩個null.
這是因為如果對象不屬於Guice托管, 那么他也無法得到Guice注入.
如果一定要new GuiceObjectDemo()呢? 沒關系, 還有另外一種寫法可以滿足.
1 GuiceObjectDemo demo1 = new GuiceObjectDemo(); 2 Guice.createInjector().injectMembers(demo1); 3 System.out.println(demo1.getService());
靜態屬性注入
調用binder.requestStaticInjection
1 Guice.createInjector(new Module() { 2 @Override 3 public void configure(Binder binder) { 4 binder.requestStaticInjection(GuiceObjectDemo.class); 5 } 6 }); 7 System.out.println(GuiceObjectDemo.getService3());
普通屬性也可以通過該方法注入, 只要把binder那邊改成requestInjection即可.
構造函數注入
1 @Inject 2 public GuiceObjectDemo(TestService service1, TestService service2) { 3 this.service1 = service1; 4 this.service2 = service2; 5 }
構造函數會自動注入多個參數, 因此只要寫一個@Inject即可.
如果有多個構造函數, 只能在一個構造函數上加Inject, 不然會報錯
has more than one constructor annotated with @Inject
同理Setter注入, 只要在setXX方法上加上@Inject標簽即可實現賦值.
動態參數注入
這個稍微麻煩一點, 需要引入guice-assistedinject, 利用FactoryModuleBuilder構造一個factory實行注入.
實際業務場景中, 大部分構造函數的參數是動態從外部傳遞進來的, 並不是直接new出來的.
1 public class ServiceImpl implements TestService{ 2 3 private String member; 4 5 @Inject 6 public ServiceImpl(@Assisted String member) { 7 // 利用Assisted注解, 動態注入參數 8 this.member = member; 9 } 10 11 public void setMember(String member) { 12 this.member = member; 13 } 14 15 @Override 16 public String toString() { 17 return "ServiceImpl Memeber: " + member; 18 } 19 } 20 --------------------------------------- 21 public interface TestService { 22 23 } 24 --------------------------------------- 25 public interface PageFactory { 26 27 ReportPageProvider createReportPage(ResultReport report); 28 29 } 30 --------------------------------------- 31 public class IOCDemo { 32 33 public static void main(String[] args){ 34 Module module = new com.fr.third.inject.Module() { 35 @Override 36 public void configure(Binder binder) { 37 binder.install(new FactoryModuleBuilder() 38 .implement(TestService.class, ServiceImpl.class) 39 .build(ImplFactory.class) 40 ); 41 } 42 }; 43 44 Injector injector = Guice.createInjector(module); 45 ImplFactory factory = injector.getInstance(ImplFactory.class); 46 TestService impl = factory.create("neil123"); 47 System.out.println(impl); 48 } 49 50 }
有多個實現類的接口
此時通過上文直接寫單個@Inject或者Module都無法實現, 需要引入自定義注解, 或者Names方法.
1 public class GuiceObjectDemo { 2 3 @Inject 4 @Named("A") 5 private TestService service1; 6 @Inject 7 @Named("B") 8 private TestService service2; 9 10 --------------------------------------- 11 12 final GuiceObjectDemo demo1 = new GuiceObjectDemo(); 13 Guice.createInjector(new Module() { 14 @Override 15 public void configure(Binder binder) { 16 binder.bind(TestService.class).annotatedWith(Names.named("A")).to(ServiceImplA.class); 17 binder.bind(TestService.class).annotatedWith(Names.named("B")).to(ServiceImplB.class); 18 binder.requestInjection(demo1); 19 } 20 }); 21 System.out.println(demo1.getService()); 22 System.out.println(demo1.getService2());
如果不用Named注解, 則可以通過自定義注解, 其他寫法都一樣
1 binder.bind(TestService.class).annotatedWith(ImplA.class).to(ServiceImplA.class); 2 binder.bind(TestService.class).annotatedWith(ImplB.class).to(ServiceImplB.class);
Provider注入
其實就是類似於工廠注入, 對象不是直接new接口的實現類, 而是由工廠提供.
1 public class ServiceFactory implements Provider<TestService> { 2 3 @Override 4 public TestService get() { 5 return new ServiceImpl(); 6 } 7 8 } 9 10 --------------------------------------- 11 12 @ProvidedBy(ServiceFactory.class) 13 public interface TestService { 14 15 void test(); 16 } 17 18 --------------------------------------- 19 20 GuiceObjectDemo demo = Guice.createInjector().getInstance(GuiceObjectDemo.class); 21 System.out.println(demo.getService());
Scope
可以通過在impl類上加@Singleton來實現單例, 也可在module中管理
1 binder.bind(TestService.class).to(ServiceImpl.class).in(Scopes.SINGLETON);
默認單例模式的對象, 是在第一次使用的時候才初始化, 也可以通過設置asEagerSingleton, 注入到容器后立刻初始化.
1 Injector in = Guice.createInjector(new Module() { 2 @Override 3 public void configure(Binder binder) { 4 // 調用getInstance才初始化impl 5 binder.bind(ServiceImpl.class); 6 // 注入到容器后立刻初始化impl 7 // binder.bind(ServiceImpl.class).asEagerSingleton(); 8 } 9 }); 10 Thread.sleep(3000); 11 in.getInstance(ServiceImpl.class).test();
到這邊就結束了, 通過上面的案例不難看出, , 相比於Spring IOC, Guice是一個非常輕量靈活的注入實現, 0 xml.