一、简介
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();
}
...
分享到:
相关推荐
通过分析Guice 3.0的源码,我们可以深入了解其内部的工作原理,包括如何解析注解、如何构建对象图、如何处理依赖关系等。这对于理解依赖注入原理,以及优化和自定义Guice的行为都是非常有价值的。同时,阅读源码也有...
**Google Guice**,全称为Google Injection,是一个轻量级的依赖注入框架,它通过注解(Annotations)来实现对象的自动装配,简化了Java应用的构造和管理。Guice的核心理念是帮助开发者摆脱手动创建对象和管理对象...
标题所指的知识点为“Druid 源码分析 逐层详解”,意味着我们需要深入分析Druid这一开源数据处理工具的源码,并从不同的层面揭示其内部实现机制。 首先,我们来看Druid的构架设计。Druid采用了分层的架构,每个层次...
当我们调用`Injector.create`并传入一个模块实例时,Guice会分析模块的配置,生成对象图。然后,我们可以通过注入器来获取任何已配置的依赖对象,无需手动创建。 3. **注解(Annotations):** Guice使用注解来标识...
MyBatis-Guice 是一个将 MyBatis ORM 框架与 Google Guice 依赖注入框架整合的...通过学习和分析这些代码,开发者可以深入理解如何在实际项目中集成 MyBatis 和 Guice,以及它们如何协同工作以提升开发效率和代码质量。
2. 配置FindBugs:设置FindBugs插件,指定要扫描的源代码路径、排除的文件和类,以及要使用的检测规则。 3. 运行分析:执行构建过程,FindBugs-Guice会自动进行代码分析,并生成报告,指出可能存在的问题。 4. ...
在深入研究之前,你需要解压文件并查看源代码,了解它们是如何协作完成特定功能的。可能的实践场景包括用户登录验证、角色权限管理、数据展示等。没有jar包意味着你需要从其他来源获取必要的库,例如Guice和MyBatis...
【标签】"开源项目" 表明上述两个工具——NFSDB-Guice集成和WartRemover——都是开放源代码的,这意味着它们的源代码可供公众查看、使用、修改和分发。开源项目鼓励社区协作,开发者可以通过参与开源项目来学习、...
6. **Git仓库结构**:"org.liveSense.misc.configurationLoader-master"可能是Git仓库的本地克隆,其中包含了项目的所有源代码文件,如Java源码、资源文件、构建脚本(如pom.xml或build.gradle)、测试代码以及...
标签 "开源项目" 表明了这个软件是开放源代码的,这意味着任何人都可以查看、使用、修改和分发它的源代码,符合开源社区的共享和协作精神。 压缩包子文件 "slicer-master" 暗示了这是"slicer"项目的主分支或者是最...
4. 分析测试套件的组织结构,学习如何编写高效且易于维护的测试代码。 5. 研究示例测试用例,了解如何编写针对具体业务逻辑的测试,确保代码质量。 总的来说,掌握 `JUnitGuiceMockito` 高效组合测试框架不仅可以...
通常,这样的命名表示这是一个开源项目,"master"指的是Git仓库的主要分支,包含项目的完整源代码和资源。 综合以上分析,这个入门套件可能包含以下内容: 1. 使用Jersey构建RESTful API的服务端代码。 2. Guice的...
总的来说,"基于java的开发源码-UIMA注解类 uimaFIT.zip"包含的源代码示例展示了如何利用uimaFIT简化UIMA组件的开发,以及如何通过Java注解来定义和配置UIMA分析引擎。通过深入学习和实践这些源代码,开发者可以更好...
【压缩包子文件的文件名称列表】"dropwizard-guice-master" 提示我们,这是一个 Git 仓库的主分支,其中包含了 dropwizard-guice 库的源代码。通常,"master" 分支是开发的主要分支,代表了项目的最新稳定状态。在这...
源代码展示了如何构建自定义的Guice模块来启用Metrics集成,同时也提供了如何在应用中使用这些注解的实例。文档部分通常会详细介绍如何配置和使用metrics-guice,包括安装步骤、基本用法以及高级特性的介绍。通过...
GWT 是一个用于构建富互联网应用程序(RIA)的开放源代码Java框架,它允许开发者使用Java编程语言来编写前端应用,然后通过编译器将Java代码转换为优化过的JavaScript。在GWT中,反射是一个强大的工具,允许程序在...
通过分析`todolist`目录下的源代码,我们可以学习到如何在Play Framework 2.0中创建一个简单应用,包括如何处理HTTP请求,如何定义模型和视图,以及如何使用数据库。这个教程为初学者提供了一个很好的起点,让他们...