深入剖析Guice(Google依赖注入框架)
術語
Guice:整個框架的門面
Injector:一個依賴的管理上下文
Binder:一個接口和實現的綁定
Module:一組Binder
Provider:bean的提供者
Key:Binder中對應一個Provider
Scope:Provider的作用域
Stage:運行方式(為了不同的要求)
使用實例
public class FooApplication {
    public static void main(String[] args) {
        Injector injector = Guice.createInjector(
            new ModuleA(),
            new ModuleB(),
            . . .
            new FooApplicationFlagsModule(args)
        );
        // Now just bootstrap the application and you're done
        FooStarter starter = injector.getInstance(FooStarter.class);
        starter.runApplication();
    }
}
關于Stage以及如何選擇?
stage分為三種,是三個不同場景下對應的值,選擇Stage.TOOL時一些操作則不會支持,例如:java.lang.UnsupportedOperationException:Injector.injectMembers(Object) is not supported in Stage.TOOL
TOOL(最小代價,有些功能會無法使用)
DEVELOPMENT(快速啟動,但不會做校驗)
PRODUCTION(異常檢查與性能,啟動會比較慢)
Guice.createInjector(Stage.PRODUCTION, new ModuleA()); Guice.createInjector(new ModuleA());//默認DEVELOPMENT
Module(模塊)
  Module內利用BindingBuilder生成Binder,如何實現一個Module?
public class EventModule extends AbstractModule {
    @Override
    protected void configure() {
    bind(EventBusManager.class).annotatedWith(Names.named("eventBusManager")).to(EventBusManager.class);
    }
}
幾種bind方式
bind(EventService.class).to(SpringEventService.class);
bind(EventService.class).toInstance(new SpringEventService());
bind(SpringEventService.class).asEagerSingleton();
bind(EventService.class).toProvider(new Provider<EventService>(){    
    @Override    
    public EventService get(){        
       return new SpringEventService();    
    }
});
bind(EventService.class).toConstructor((Constructor<SpringEventService>)SpringEventService.class.getConstructors()[0]);
注意:第一個直接to(implemet)的方式是getProvider時new出的Provider,第二種是用Providers.of(instance)生成的ConstantProvider
注入依賴@Inject
@Singleton
public class MyHandler {    
   @Inject    
   @Named("springEventService")    
   private EventService eventService;    
   @Subscribe    
   public void handleEvent(MyEvent event) {     
      eventService.post("MyHandler",event);    
   }
}
@Inject和@Named結合使用達到按名字注入,@Inject的optional默認為false,注入時如果依賴不存在,則報錯停止,當使用@Inject(optional = true)時可達到忽視依賴是否存在的效果
實例創建
FooStarter starter = injector.getInstance(FooStarter.class);
如果injector已經有則直接返回已有對象,沒有則創建一個(利用依賴注入,如果沒有綁定過則不會被依賴注入),默認prototype模式(每次都新建一個),@Singleton可以指定singleton模式
getInstance的過程
  先根據指定的class類new Key(),Key包括class信息和注解信息,class的hashcode和注解的hashcode決定了Key的hashcode,getProvider時是根據Key的hashcode來判斷是否是同一個Key,然后取到Provider,由Provider提供最終的instance
注意:無注解時會有一個默認的hashcode
Key的hashcode計算公式
class.hashcode * 31 + annotation.hashcode
會出現多個組合得到的hashcode是相同的情況么?
2 * 31 + 3 = 65
1 * 31 + 34 = 65
為什么用這樣的公式?(java中String是如何計算hashcode的?)
Joshua Bloch's Effective Java中是這樣解釋的:
The value 31 was chosen because it is an odd prime. If it were even and the multiplication overflowed, information would be lost, as multiplication by 2 is equivalent to shifting. The advantage of using a prime is less clear, but it is traditional. A nice property of 31 is that the multiplication can be replaced by a shift and a subtraction for better performance: 31 * i == (i << 5) - i. Modern VMs do this sort of optimization automatically.(from Chapter 3, Item 9: Always override hashcode when you override equals, page 48)
選擇值31是因為它是奇數。 如果是偶數并且乘法溢出,則信息將丟失,因為乘以2等效于移位。 使用素數的優點不太清楚,但它是傳統的。 31的一個好的屬性是乘法可以由移位和減法替換以獲得更好的性能:31 * i ==(i << 5) - i。 現代虛擬機自動進行這種優化。(從第3章,項9:覆蓋equals時始終覆蓋hashcode,第48頁)
參考:
http://stackoverflow.com/questions/299304/why-does-javas-hashcode-in-string-use-31-as-a-multiplier
https://www.cs.cmu.edu/~adamchik/15-121/lectures/Hashing/hashing.html
Guice對于classloader的依賴有多重要?
由于一個class被加載后是否唯一由加載的classloader決定,所以不同的classloader加載同一個class會生成兩個class實例(反射中一個class也會有一個實例),兩個不同的class生成的Key的hashcode則不同,所以在Guice中根據Key來獲取時必須要用同一個classloader加載的類,否則獲取不到,所以在OSGI方式下用Guice需要注意
injector.injectMembers(instance)
Injector injectorBase = Guice.createInjector(new EventModule()); Injector injector = injectorBase.createChildInjector(new SpringModule()); MyHandler handler = new MyHandler();// eventService is null injector.injectMembers(handler);// eventService use instance
用一個已經有的實例,但依賴的對象為null,這時可以用injector注入依賴對象,但這個實例不會有綁定關系,所以如果其他有需要依賴這個實例的也無法注入這個實例
Injector繼承
Injector parent = Guice.createInjector(new EventModule()); Injector child = parent .createChildInjector(new SpringModule());
child 可以依賴parent ,但反過來則不可以
依賴范圍
  一個Injector中如果包含了多個Module,各Module中的是可以相互使用的,也就是可以相互依賴
  如果一個Injector想依賴另一個Injector中的實例,那就要通過繼承了,例如功能模塊想要依賴基礎模塊,那功能模塊可以繼承基礎模塊
依賴Tree
AOP in Guice
Binder#bindInterceptor(Matcher<? super Class<?>> classMatcher,    Matcher<? super Method> methodMatcher,    org.aopalliance.intercept.MethodInterceptor... interceptors)
bindInterceptor(Matchers.any(),Matchers.annotatedWith(Named.class),new MethodInterceptor(){    
@Override    
public Object invoke(MethodInvocation invocation) throws Throwable {        
   System.out.println("do something before...");
   Object result = invocation.proceed();        
   System.out.println("do something after...");        
   return result;    
   }
});
Matcher通過Matchers來生成
與spring整合
如何解決這種相互依賴?
達到的效果:
處理過程:
代碼:
將spring中的bean暴露給Guice:
public class SpringModule extends AbstractModule {    
   private ConfigurableListableBeanFactory beanFactory;    
   public SpringModule(ConfigurableListableBeanFactory beanFactory){        
      this.beanFactory = beanFactory;    }    
      @Override    
      protected void configure() {        
         bind(BeanFactory.class).toInstance(this.beanFactory);        
         String[] names = this.beanFactory.getBeanDefinitionNames();        
         for (String name : names) {            
            try {                
               Object instance = this.beanFactory.getBean(name);                
               Class clazz = instance.getClass();                
               bind(clazz).toInstance(instance);                
               Class[] intefaces = clazz.getInterfaces();                
               for (Class inteface: intefaces) {                    
                  if (!inteface.getName().contains("com.xxxxxx")) {                        
                     continue;                    
                  }                    
                  bind(inteface).annotatedWith(Names.named(name)).toInstance(instance);                
               }                
               bind(clazz).annotatedWith(Names.named(name)).toInstance(instance);            
            } catch (Exception e) {            
         }        
      }    
   }
}
將Guice里的beans暴露給spring:
@Component
public class PbfInitProcesser implements BeanFactoryPostProcessor {    
   @Override    
   public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {        
      // map injector to spring beanFactory        
      Injector spring = Guice.createInjector(new SpringModule(beanFactory));        
      Injector injector = spring.createChildInjector(new ProductModule());        
      PbfEnvInitUtil.shareGuiceToSpring("injector", beanFactory, injector);    
   }
}
public class PbfEnvInitUtil {    
   public static final void refresh(ApplicationContext context) {        
      // refresh inject bean to autowire        
      String names[] = context.getBeanDefinitionNames();        
      for (String name : names) {            
         context.getAutowireCapableBeanFactory().autowireBean(context.getBean(name));        
      }    
   }    
   public static void shareGuiceToSpring(String bizName, ConfigurableListableBeanFactory beanFactory, Injector injector) {        
      Map<Key<?>, Binding<?>> map = injector.getAllBindings();        
      Iterator<Map.Entry<Key<?>, Binding<?>>> iterator = map.entrySet().iterator();        
      while (iterator.hasNext()) {            
         Map.Entry<Key<?>, Binding<?>> entry = iterator.next();            
         Binding<?> binding = entry.getValue();            
         Object listener = binding.getProvider().get();            
         Annotation annotation = entry.getKey().getAnnotation();            
         if (null != annotation && annotation instanceof Named) {                
            String name = ((Named)annotation).value();                
            try {                    
               beanFactory.registerSingleton(name, listener);                
            } catch (Exception e) {                
            }            
         }         
      }        
      beanFactory.registerSingleton(bizName, injector);    
   }
}
springboot中使用:
ApplicationContext context = SpringApplication.run(Application.class, args); PbfEnvInitUtil.refresh(context);
總結
以上是生活随笔為你收集整理的深入剖析Guice(Google依赖注入框架)的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        