热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

术语Guice:整个框架的门面Injector:一个依赖的管理上下文Binder:一个接口和实现的绑定Module:一组BinderProv

术语

  • 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(){
@Override
public EventService get(){
return new SpringEventService();
}
});
bind(EventService.class).toConstructor((Constructor)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> classMatcher,    Matcher 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:
@Componentpublic 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, Binding> map = injector.getAllBindings();              Iterator, Binding>> iterator = map.entrySet().iterator();              while (iterator.hasNext()) {                     Map.Entry, 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);




推荐阅读
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • 本文介绍了在CentOS 6.4系统中更新源地址的方法,包括备份现有源文件、下载163源、修改文件名、更新列表和系统,并提供了相应的命令。 ... [详细]
author-avatar
手机用户2502895261
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有