• 深入剖析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);
  • 相关阅读:
    侠客博客v1.0 正式版版本发布
    酒店分销赚钱
    备份VPS 每周同步文件
    关于伪原创编辑的技巧
    在线考试系统,按计划一点一点的开发。
    WORDPRESS”丢失计划任务”
    钦和SEO服务DLL
    ORM之MySoft_Data测试成功。应该是非常好用的。
    发送了50左右篇博客文章
    writeFlashHTML,一个JS方法,主要用于Flash的输出。
  • 原文地址:https://www.cnblogs.com/ms-grf/p/6826097.html
Copyright © 2020-2023  润新知