Guice源码学习基本原理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Guice源码学习基本原理相关的知识,希望对你有一定的参考价值。

Guice是Google开发的一个开源轻量级的依赖注入框架,运行速度快,使用简单。

 

项目地址:https://github.com/google/guice/

最新的版本是4.1,本文基于此版本。

Guice的使用方法请参见我的前篇博文:《Guice 4.1教程

 

0. Guice的使用范例

先分析最简单的例子

public interface Dog {
    void bark();
}

public class BlackDog implements Dog{
    @Override
    public void bark() {
        System.out.println("i am black dog");
    }
}

public class GuiceTest {
    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new Module() {
            @Override
            public void configure(Binder binder) {
                binder.bind(Dog.class).to(BlackDog.class);
            }
        });

        injector.getInstance(Dog.class).bark();
    }
}

从上面的代码中,我们可以清楚的看出,Guice先利用自定义的Module创建了一个Injector,然后调用这个Injector的getInstance方法获得了Dog接口的BlackDog子类的一个实例

Dog接口与BlackDog子类的的绑定,则是在Module.configure方法中设置的。

 

1. Guice的本质

在分析源码之间,我们不妨先考虑一下Guice的本质。

Q:Guice主要解决的是什么问题?

A:依赖注入的问题

Q:怎么知道哪里的依赖会被注入?

A:被@Injector注解修饰的属性/构造方法/Setter方法会触发注入

Q:Guice怎么知道该注入什么实例?

A:初始化Injector时传入的Module对象,或者@ImplementedBy等注解,以及属性的注解都会限定被注入的对象的类型,如果不能限定到某种唯一特定的类型,Guice会抛出错误

 

这样我们就有一个大概的猜想了,Guice内部肯定维护了从接口到实现类的关系

其形式应该类似于一个Key为接口的class,value为实现类的Map

每次应用调用getInstance想要获取某个接口的实现类的实例的时候,就会去这个Map里检索,找到对应的实现类后调用其构造方法并返回

如果实现类中又有需要被注入的属性,则递归调用这一注入过程即可。

 

实际上,Guice内部确实维护了接口到实现类(也可能是Provider或者某个实例对象)的映射。

但是考虑到泛型与注解,Guice不是直接用接口的class作为key,而是用com.google.inject.Key来描述某个可以被注入的接口的,com.google.inject.Key的定义如下所示

public class Key<T> {

  private final AnnotationStrategy annotationStrategy;//注解限定

  private final TypeLiteral<T> typeLiteral;//类型限定
  private final int hashCode;
  private String toString;
}

 

Key内部有两个关键属性,annotationStrategy与typeLiteral,分别表示这个接口的注解限定与类型限定。

以本文开头部分的场景为例,Dog接口对应的Key中,annotationStrategy就是NullAnnotationStrategy(无注解),typeLiteral则为com.cc.test.guice.Dog(Dog接口的全称)

在createInjector函数中,Guice会完成从Dog接口到BlackDog实现类的绑定

而在getInstance方法中,传入的Dog.class也会被组装成Key对象,Guice很容易就可以根据根据这个Key对象找到正确的实现类BlackDog,这样就可以调用构造方法创建对象并返回了

 

 

2. Guice.createInjector方法的调用链

  public static Injector createInjector(Module... modules) {
    return createInjector(Arrays.asList(modules));
  }

  public static Injector createInjector(Iterable<? extends Module> modules) {
    return createInjector(Stage.DEVELOPMENT, modules);
  }

  public static Injector createInjector(Stage stage,
      Iterable<? extends Module> modules) {
    return new InternalInjectorCreator()
        .stage(stage)
        .addModules(modules)
        .build();
  }

从这里我们可以看出,Guice会利用应用指定的Module配合InternalInjectorCreator构建出一个Injector对象。

 

以上是关于Guice源码学习基本原理的主要内容,如果未能解决你的问题,请参考以下文章

java轻量级IOC框架Guice

java轻量级IOC框架Guice

学习底层原理系列重读spring源码1-建立基本的认知模型

《Druid源码解析 Guice和Realtime流程》——图较精简,不错

Apache Druid源码导读--Google guice DI框架

Apache Druid源码导读--Google guice DI框架