`
zhuhui_zj
  • 浏览: 36624 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Guice源代码分析(一)

阅读更多

一、简介

    Guice中通过自定义Module实现中的bind、to等语法实现绑定信息的注册,在绑定中存在着一种特殊的情形:范型绑定,如下:

public class ListUser { 
    @Inject @Named("list") List<String> strings; 
    @Inject @Named("list") List<Integer> integers; 
}

    Java的范型实现采用了擦除的方式 ,为此无法在运行时区分List<String>和List<Integer>类,因为模板参数的信息在运行时都已经被擦除了。为了能在运行时得到模板参数的信息,Guice引入了TypeLiteral类,它代表类的型别信息,在Guice文档中将其称为"Super Type Tokens"。

    Guice运行时的一个核心数据结构是存储绑定信息的一个Map实例,该Map将一个接口以及附加的注释映射到实现类上。在Guice中通过Key这个类来标示唯一的绑定信息。

二、TypeLiteral类

    TypeLiteral类主要的目的是在运行时得到模板参数的信息,并以统一的方式代表非范型类和范型类的型别。

public abstract class TypeLiteral<T> {

  final Class<? super T> rawType;
  final Type type;
  final int hashCode;

  ...
}

    TypeLiteral是抽象类,其中的rawType代表擦除模板参数后的普通类型,如List<Integer>在擦除之后的rawType是List。type代表完整的类型信息,可以是Class或ParameterizedType。

2.1、非范型的情况

    为了代表非范型类,TypeLiteral使用内联的SimpleTypeLiteral:

public abstract class TypeLiteral<T> {

  @SuppressWarnings("unchecked")
  TypeLiteral(Type type) {
    this.rawType = (Class<? super T>) getRawType(nonNull(type, "type"));
    this.type = type;
    this.hashCode = hashCode(type);
  }

  private static class SimpleTypeLiteral<T> extends TypeLiteral<T> {
    public SimpleTypeLiteral(Type type) {
      super(type);
    }
  }

  ...
}

    可以通过TypeLiteral.get(...)工厂方法得到普通类的TypeLiteral:

  public static <T> TypeLiteral<T> get(Class<T> type) {
    return new SimpleTypeLiteral<T>(type);
  }

2.2、范型类的TypeLiteral   

    为了让TypeLiteral在运行时得到范型类的模板参数信息,必须采用如下特殊的方法:

 

public class StringListTypeLiteral extends TypeLiteral<List<String>>{
}

public abstract class TypeLiteral<T> {

  @SuppressWarnings("unchecked")
  protected TypeLiteral() {
    this.type = getSuperclassTypeParameter(getClass());
    this.rawType = (Class<? super T>) getRawType(type);
    this.hashCode = hashCode(type);
  }

  ...
}

    StringListTypeLiteral初始化时调用TypeLiteral的默认constructor,它可以获得继承时TypeLiteral<T>中模板参数T(例子中是List<String>)的信息。实现中是通过使用范型反射的方法:

public abstract class TypeLiteral<T> {

  static Type getSuperclassTypeParameter(Class<?> subclass) {
    Type superclass = subclass.getGenericSuperclass();
    if (superclass instanceof Class) {
      throw new RuntimeException("Missing type parameter.");
    }
    // Java5中新引入的一种针对范型的反射语法,可以得到继承时父类的模板参数信息
    // 在Hibernate的Generic DAO中也有用到,是一个获得模板参数信息的特殊方法
    return ((ParameterizedType)superclass).getActualTypeArguments()[0];
  }


  @SuppressWarnings({ "unchecked" })
  private static Class<?> getRawType(Type type) {
    if (type instanceof Class<?>) {
      // 是非范型类      
      return (Class<?>) type;
    }
    else {
      if (type instanceof ParameterizedType) {
        ParameterizedType parameterizedType = (ParameterizedType) type;

        Type rawType = parameterizedType.getRawType();
        if (!(rawType instanceof Class<?>)) {
          throw unexpectedType(rawType, Class.class);
        }
        return (Class<?>) rawType;
      }

      if (type instanceof GenericArrayType) {
        return Object[].class;
      }

      throw unexpectedType(type, ParameterizedType.class);
    }
  } 
  ...
}

    针对StringListTypeLiteral,getSuperclassTypeParameter(..)方法返回的是代表List<String>的ParameterizedType。再对得到的type调用getRawType(..)得到Class<List>。

2.3、类型相等的判定

    当用TypeLiteral取代Class作为Type Tokens时,还需要解决如何判定两个类型相等的问题,自然通过重载equals(..)方法:

public abstract class TypeLiteral<T> {

  public boolean equals(Object o) {
    if (o == this) {
      return true;
    }
    if (!(o instanceof TypeLiteral<?>)) {
      return false;
    }
    TypeLiteral<?> other = (TypeLiteral<?>) o;

    return equals(type, other.type);
  }

  static boolean equals(Type a, Type b) {
    if (a instanceof Class) {
      // 非范型类
      return a.equals(b);
    }

    // 针对范型类,可能存在嵌套的模板参数,如List<Map<Integer,List<Double>>>
    //故需要递归调用
    if (a instanceof ParameterizedType) {
      if (!(b instanceof ParameterizedType)) {
        return false;
      }

      ParameterizedType pa = (ParameterizedType) a;
      ParameterizedType pb = (ParameterizedType) b;

      // 擦除后类型比较
      if (!pa.getRawType().equals(pb.getRawType())) {
        return false;
      }

      Type[] aa = pa.getActualTypeArguments();
      Type[] ba = pb.getActualTypeArguments();
      if (aa.length != ba.length) {
        return false;
      }

      for (int i = 0; i < aa.length; i++) {
        // 递归调用
        if (!equals(aa[i], ba[i])) {
          return false;
        }
      }

      return true;
    }

    ...
    return false;
  }

2.4、范型绑定问题的解决

    为了区别List<Integer>和List<String>的绑定,我们需要使用TypeLiteral。简洁的使用方法是匿名类:

public class TypeLiteralModule extends AbstractModule { 
    protected void configure() { 
        bind(new TypeLiteral<List<String>>(){}) 
            .annotatedWith(Names.named("list")) 
            .to(new TypeLiteral<ArrayList<String>>(){}); 

        bind(new TypeLiteral<List<Integer>>(){}) 
            .annotatedWith(Names.named("list")) 
            .to(new TypeLiteral<ArrayList<Integer>>(){}); 
    } 
}

三、Key类

    在Guice的Injector中为了唯一确定一个绑定信息,需要一个绑定类型(一般为一个Interface的Class)和一个可选的Annotation。Key类就是将两者封装作为一个完整的键值,用于在Injector内部的Map中存取绑定信息。

public abstract class Key<T> {

  final AnnotationStrategy annotationStrategy;
  final TypeLiteral<T> typeLiteral;
  final int hashCode;
  
  ...
}

    AnnotationStrategy是一个内联接口,它代表一个Annotation;TypeLiteral是绑定类型。Key也是一个抽象类,为了得到它的实例仍然需要通过get(..)工厂方法。Key中一个SimpleKey内联类是Key的简单实现。

public abstract class Key<T> {

  @SuppressWarnings("unchecked")
  private Key(Type type, AnnotationStrategy annotationStrategy) {
    this.annotationStrategy = annotationStrategy;
    this.typeLiteral = (TypeLiteral<T>) TypeLiteral.get(type);
    this.hashCode = computeHashCode();
  }

  private static class SimpleKey<T> extends Key<T> {

    private SimpleKey(Type type, AnnotationStrategy annotationStrategy) {
      super(type, annotationStrategy);
    }

    ...
  }

  ...
}

    一个简单工厂方法的实现:

  public static <T> 
  Key<T> get(Class<T> type,
      Class<? extends Annotation> annotationType) {
    return new SimpleKey<T>(type, strategyFor(annotationType));
  }

    作为键值最关键的两个方法:equals和hashCode的实现也很直接:

public abstract class Key<T> {

  public boolean equals(Object o) {
    if (o == this) {
      return true;
    }
    if (!(o instanceof Key<?>)) {
      return false;
    }
    Key<?> other = (Key<?>) o;
    //  绑定类型和注释完全相等
    return annotationStrategy.equals(other.annotationStrategy)
        && typeLiteral.equals(other.typeLiteral);
  }

  private int computeHashCode() {
    return typeLiteral.hashCode() * 31 + annotationStrategy.hashCode();
  }

  ...

 

分享到:
评论

相关推荐

    google guice 3.0源码

    通过分析Guice 3.0的源码,我们可以深入了解其内部的工作原理,包括如何解析注解、如何构建对象图、如何处理依赖关系等。这对于理解依赖注入原理,以及优化和自定义Guice的行为都是非常有价值的。同时,阅读源码也有...

    Google guice

    **Google Guice**,全称为Google Injection,是一个轻量级的依赖注入框架,它通过注解(Annotations)来实现对象的自动装配,简化了Java应用的构造和管理。Guice的核心理念是帮助开发者摆脱手动创建对象和管理对象...

    druid 源码分析 逐层详解

    标题所指的知识点为“Druid 源码分析 逐层详解”,意味着我们需要深入分析Druid这一开源数据处理工具的源码,并从不同的层面揭示其内部实现机制。 首先,我们来看Druid的构架设计。Druid采用了分层的架构,每个层次...

    Guice用户指南翻译

    当我们调用`Injector.create`并传入一个模块实例时,Guice会分析模块的配置,生成对象图。然后,我们可以通过注入器来获取任何已配置的依赖对象,无需手动创建。 3. **注解(Annotations):** Guice使用注解来标识...

    mybatis-guice

    MyBatis-Guice 是一个将 MyBatis ORM 框架与 Google Guice 依赖注入框架整合的...通过学习和分析这些代码,开发者可以深入理解如何在实际项目中集成 MyBatis 和 Guice,以及它们如何协同工作以提升开发效率和代码质量。

    findbugs-guice-0.5.zip

    2. 配置FindBugs:设置FindBugs插件,指定要扫描的源代码路径、排除的文件和类,以及要使用的检测规则。 3. 运行分析:执行构建过程,FindBugs-Guice会自动进行代码分析,并生成报告,指出可能存在的问题。 4. ...

    ext extjs4学习经典实例 guice mybaits 简单权限 (无ext资源包和jar包)

    在深入研究之前,你需要解压文件并查看源代码,了解它们是如何协作完成特定功能的。可能的实践场景包括用户登录验证、角色权限管理、数据展示等。没有jar包意味着你需要从其他来源获取必要的库,例如Guice和MyBatis...

    nfsdb-guice-1.0.zip

    【标签】"开源项目" 表明上述两个工具——NFSDB-Guice集成和WartRemover——都是开放源代码的,这意味着它们的源代码可供公众查看、使用、修改和分发。开源项目鼓励社区协作,开发者可以通过参与开源项目来学习、...

    nfsdb-guice-1.0.2.zip

    6. **Git仓库结构**:"org.liveSense.misc.configurationLoader-master"可能是Git仓库的本地克隆,其中包含了项目的所有源代码文件,如Java源码、资源文件、构建脚本(如pom.xml或build.gradle)、测试代码以及...

    wink-guice-server-1.2.1-incubating.zip

    标签 "开源项目" 表明了这个软件是开放源代码的,这意味着任何人都可以查看、使用、修改和分发它的源代码,符合开源社区的共享和协作精神。 压缩包子文件 "slicer-master" 暗示了这是"slicer"项目的主分支或者是最...

    JUnitGuiceMockito高效组合测试框架

    4. 分析测试套件的组织结构,学习如何编写高效且易于维护的测试代码。 5. 研究示例测试用例,了解如何编写针对具体业务逻辑的测试,确保代码质量。 总的来说,掌握 `JUnitGuiceMockito` 高效组合测试框架不仅可以...

    api-start:球衣+guice+mybatis+gradle+liquibase 入门套件

    通常,这样的命名表示这是一个开源项目,"master"指的是Git仓库的主要分支,包含项目的完整源代码和资源。 综合以上分析,这个入门套件可能包含以下内容: 1. 使用Jersey构建RESTful API的服务端代码。 2. Guice的...

    基于java的开发源码-UIMA注解类 uimaFIT.zip

    总的来说,"基于java的开发源码-UIMA注解类 uimaFIT.zip"包含的源代码示例展示了如何利用uimaFIT简化UIMA组件的开发,以及如何通过Java注解来定义和配置UIMA分析引擎。通过深入学习和实践这些源代码,开发者可以更好...

    titan-all-0.3.0.zip

    【压缩包子文件的文件名称列表】"dropwizard-guice-master" 提示我们,这是一个 Git 仓库的主分支,其中包含了 dropwizard-guice 库的源代码。通常,"master" 分支是开发的主要分支,代表了项目的最新稳定状态。在这...

    httpz-dispatch_2.11-0.2.10-RC1-scalaz70.zip

    源代码展示了如何构建自定义的Guice模块来启用Metrics集成,同时也提供了如何在应用中使用这些注解的实例。文档部分通常会详细介绍如何配置和使用metrics-guice,包括安装步骤、基本用法以及高级特性的介绍。通过...

    gwt反射 http://code.google.com/p/google-gin/wiki/GinTutorial

    GWT 是一个用于构建富互联网应用程序(RIA)的开放源代码Java框架,它允许开发者使用Java编程语言来编写前端应用,然后通过编译器将Java代码转换为优化过的JavaScript。在GWT中,反射是一个强大的工具,允许程序在...

    Play framework 2.0入门教程(三)的源代码,Play留言板

    通过分析`todolist`目录下的源代码,我们可以学习到如何在Play Framework 2.0中创建一个简单应用,包括如何处理HTTP请求,如何定义模型和视图,以及如何使用数据库。这个教程为初学者提供了一个很好的起点,让他们...

Global site tag (gtag.js) - Google Analytics