深入剖析Guice(Google依赖注入框架)

本文深入解析了Guice框架的核心概念,如Guice、Injector、Binder等,并提供了详细的使用示例。此外,还介绍了如何选择不同的运行阶段,以及如何实现自定义模块。最后,探讨了Guice与Spring框架之间的整合方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

术语

 

  • Guice:整个框架的门面
  • Injector:一个依赖的管理上下文
  • Binder:一个接口和实现的绑定
  • Module:一组Binder
  • Provider:bean的提供者
  • Key:Binder中对应一个Provider
  • Scope:Provider的作用域
  • Stage:运行方式(为了不同的要求)

 

使用示例

 

 

  1. public class FooApplication {  
  2.     public static void main(String[] args) {  
  3.         Injector injector = Guice.createInjector(  
  4.             new ModuleA(),  
  5.             new ModuleB(),  
  6.             . . .  
  7.             new FooApplicationFlagsModule(args)  
  8.         );  
  9.         // Now just bootstrap the application and you're done  
  10.         FooStarter starter = injector.getInstance(FooStarter.class);  
  11.         starter.runApplication();  
  12.     }  
  13. }  

 

关于Stage以及如何选择?

stage分为三种,是三个不同场景下选择对应的值,Stage.TOOL时一些操作则不会支持,例如:java.lang.UnsupportedOperationException:Injector.injectMembers(Object) is not supported in Stage.TOOL

 

  • TOOL(最小代价,有些功能会无法使用)
  • DEVELOPMENT(快速启动,但不会做校验)
  • PRODUCTION(异常检查与性能,启动会比较慢)

 

  1. Guice.createInjector(Stage.PRODUCTION, new ModuleA());  
  2. Guice.createInjector(new ModuleA());//默认DEVELOPMENT  

 

Module(模块)

 

 

Module内利用BindingBuilder生成Binder,如何实现一个Module?

 

 

 

  1. public class EventModule extends AbstractModule {  
  2.     @Override  
  3.     protected void configure() {  
  4. <span style="white-space:pre">    </span>// 绑定接口与实现  
  5.      <span style="white-space:pre">   </span>bind(EventBusManager.class).annotatedWith(Names.named("eventBusManager")).to(EventBusManager.class);  
  6.     }  
  7. }  

 

 

绑定时如果是实现类绑定实现类会报错,但如果加个别名的话就没问题

 

 

几种bind方式

 

 

  1. bind(EventService.class).to(SpringEventService.class);  
  2. bind(EventService.class).toInstance(new SpringEventService());  
  3. bind(SpringEventService.class).asEagerSingleton();  
  4. bind(EventService.class).toProvider(new Provider<EventService>(){      
  5.     @Override      
  6.     public EventService get(){          
  7.        return new SpringEventService();      
  8.     }  
  9. });  
  10. bind(EventService.class).toConstructor((Constructor<SpringEventService>)SpringEventService.class.getConstructors()[0]);  

 

 

注意:第一个直接to(implemet)的方式是getProvider时new出的Provider,第二种是用Providers.of(instance)生成的ConstantProvider

注入依赖@Inject

 

  1. @Singleton  
  2. public class MyHandler {      
  3.    @Inject      
  4.    @Named("springEventService")      
  5.    private EventService eventService;      
  6.    @Subscribe      
  7.    public void handleEvent(MyEvent event) {       
  8.       eventService.post("MyHandler",event);      
  9.    }  
  10. }  

 

@Inject和@Named结合使用达到按名字注入,@Inject的optional默认为false,注入时如果依赖不存在,则报错停止,当使用@Inject(optional = true)时可达到忽然依赖是否存在的效果

 

实例创建

 

 

 

  1. 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)

 

 

  1. Injector injectorBase = Guice.createInjector(new EventModule());  
  2. Injector injector = injectorBase.createChildInjector(new SpringModule());  
  3. MyHandler handler = new MyHandler();// eventService is null  
  4. injector.injectMembers(handler);// eventService use instance  

用一个已经有的实例,但依赖的对象为null,这时可以用injector注入依赖对象,但这个实例不会有绑定关系,所以如果其他有需要依赖这个实例的也无法注入这个实例

 

Injector继承

 

 

 

  1. Injector parent = Guice.createInjector(new EventModule());  
  2. Injector child = parent .createChildInjector(new SpringModule());  

child 可以依赖parent ,但反过来则不可以
 

 

依赖范围

 

一个Injector中如果包含了多个Module,各Module中的是可以相互使用的,也就是可以相互依赖
如果一个Injector想依赖另一个Injector中的实例,那就要通过继承了,例如功能模块想要依赖基础模块,那功能模块可以继承基础模块

依赖Tree

AOP in Guice

 

 

  1. Binder#bindInterceptor(Matcher<? super Class<?>> classMatcher,    Matcher<? super Method> methodMatcher,    org.aopalliance.intercept.MethodInterceptor... interceptors)  
  2. bindInterceptor(Matchers.any(),Matchers.annotatedWith(Named.class),new MethodInterceptor(){      
  3. @Override      
  4. public Object invoke(MethodInvocation invocation) throws Throwable {          
  5.    System.out.println("do something before...");  
  6.    Object result = invocation.proceed();          
  7.    System.out.println("do something after...");          
  8.    return result;      
  9.    }  
  10. });  

Matcher通过Matchers来生成

 

 

与spring整合

如何解决这种相互依赖?


达到的效果:

处理过程:

代码:

spring中的bean暴露给Guice:

 

 

  1. public class SpringModule extends AbstractModule {      
  2.    private ConfigurableListableBeanFactory beanFactory;      
  3.    public SpringModule(ConfigurableListableBeanFactory beanFactory){          
  4.       this.beanFactory = beanFactory;    }      
  5.       @Override      
  6.       protected void configure() {          
  7.          bind(BeanFactory.class).toInstance(this.beanFactory);          
  8.          String[] names = this.beanFactory.getBeanDefinitionNames();          
  9.          for (String name : names) {              
  10.             try {                  
  11.                Object instance = this.beanFactory.getBean(name);                  
  12.                Class clazz = instance.getClass();                  
  13.                bind(clazz).toInstance(instance);                  
  14.                Class[] intefaces = clazz.getInterfaces();                  
  15.                for (Class inteface: intefaces) {                      
  16.                   if (!inteface.getName().contains("com.xxxxxx")) {                          
  17.                      continue;                      
  18.                   }                      
  19.                   bind(inteface).annotatedWith(Names.named(name)).toInstance(instance);                  
  20.                }                  
  21.                bind(clazz).annotatedWith(Names.named(name)).toInstance(instance);              
  22.             } catch (Exception e) {              
  23.          }          
  24.       }      
  25.    }  
  26. }  

将Guice里的beans暴露给spring:

 

  1. @Component  
  2. public class PbfInitProcesser implements BeanFactoryPostProcessor {      
  3.    @Override      
  4.    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {          
  5.       // map injector to spring beanFactory          
  6.       Injector spring = Guice.createInjector(new SpringModule(beanFactory));          
  7.       Injector injector = spring.createChildInjector(new ProductModule());          
  8.       PbfEnvInitUtil.shareGuiceToSpring("injector", beanFactory, injector);      
  9.    }  
  10. }  

 

  1. public class PbfEnvInitUtil {      
  2.    public static final void refresh(ApplicationContext context) {          
  3.       // refresh inject bean to autowire          
  4.       String names[] = context.getBeanDefinitionNames();          
  5.       for (String name : names) {              
  6.          context.getAutowireCapableBeanFactory().autowireBean(context.getBean(name));          
  7.       }      
  8.    }      
  9.    public static void shareGuiceToSpring(String bizName, ConfigurableListableBeanFactory beanFactory, Injector injector) {          
  10.       Map<Key<?>, Binding<?>> map = injector.getAllBindings();          
  11.       Iterator<Map.Entry<Key<?>, Binding<?>>> iterator = map.entrySet().iterator();          
  12.       while (iterator.hasNext()) {              
  13.          Map.Entry<Key<?>, Binding<?>> entry = iterator.next();              
  14.          Binding<?> binding = entry.getValue();              
  15.          Object listener = binding.getProvider().get();              
  16.          Annotation annotation = entry.getKey().getAnnotation();              
  17.          if (null != annotation && annotation instanceof Named) {                  
  18.             String name = ((Named)annotation).value();                  
  19.             try {                      
  20.                beanFactory.registerSingleton(name, listener);                  
  21.             } catch (Exception e) {                  
  22.             }              
  23.          }           
  24.       }          
  25.       beanFactory.registerSingleton(bizName, injector);      
  26.    }  
  27. }  


springboot中使用:

 

 

 

  1. ApplicationContext context = SpringApplication.run(Application.class, args);  
  2. PbfEnvInitUtil.refresh(context);  

转载于:https://my.oschina.net/mifans/blog/783497

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值