当前位置: 首页 > 工具软件 > Joda-Beans > 使用案例 >

今日开源介绍:Google Guava、Google Guice、Joda-Time

干弘深
2023-12-01

一、Guava 是一个 Google 的基于java1.6的类库集合的扩展项目,包括 collections, caching, primitives support, concurrency libraries, common annotations, string processing, I/O, 等等. 这些高质量的 API 可以使你的JAVa代码更加优雅,更加简洁,让你工作更加轻松愉悦。下面我们就开启优雅Java编程学习之旅!

  项目相关信息:

  官方首页:http://code.google.com/p/guava-libraries
  官方下载:http://code.google.com/p/guava-libraries/downloads/list
  官方文档:http://docs.guava-libraries.googlecode.com/git/javadoc
                    http://www.ostools.net/apidocs/apidoc?api=guava

  源码包的简单说明: 
  com.google.common.annotations:普通注解类型。 
  com.google.common.base:基本工具类库和接口。 
  com.google.common.cache:缓存工具包,非常简单易用且功能强大的JVM内缓存。 
  com.google.common.collect:带泛型的集合接口扩展和实现,以及工具类,这里你会发现很多好玩的集合。 
  com.google.common.eventbus:发布订阅风格的事件总线。 
  com.google.common.hash: 哈希工具包。 
  com.google.common.io:I/O工具包。 
  com.google.common.math:原始算术类型和超大数的运算工具包。 
  com.google.common.net:网络工具包。 
  com.google.common.primitives:八种原始类型和无符号类型的静态工具包。 
  com.google.common.reflect:反射工具包。 
  com.google.common.util.concurrent:多线程工具包。

  类库使用手册:

  一.  基本工具类:让使用Java语言更令人愉悦。

  1. 使用和避免 null:null 有语言歧义, 会产生令人费解的错误, 反正他总是让人不爽。很多 Guava 的工具类在遇到 null 时会直接拒绝或出错,而不是默默地接受他们。
  2. 前提条件:更容易的对你的方法进行前提条件的测试。
  3. 常见的对象方法: 简化了Object常用方法的实现, 如 hashCode() 和 toString()。
  4. 排序: Guava 强大的 "fluent Comparator"比较器, 提供多关键字排序。
  5. Throwable类: 简化了异常检查和错误传播。

  二.  集合类:集合类库是 Guava 对 JDK 集合类的扩展, 这是 Guava 项目最完善和为人所知的部分。

  1. Immutable collections(不变的集合): 防御性编程, 不可修改的集合,并且提高了效率。
  2. New collection types(新集合类型):JDK collections 没有的一些集合类型,主要有:multisets,multimaps,tables, bidirectional maps等等
  3. Powerful collection utilities(强大的集合工具类): java.util.Collections 中未包含的常用操作工具类
  4. Extension utilities(扩展工具类): 给 Collection 对象添加一个装饰器? 实现迭代器? 我们可以更容易使用这些方法。

  三.  缓存: 本地缓存,可以很方便的操作缓存对象,并且支持各种缓存失效行为模式。

  四.  Functional idioms(函数式): 简洁, Guava实现了Java的函数式编程,可以显著简化代码。

  五. Concurrency(并发):强大,简单的抽象,让我们更容易实现简单正确的并发性代码。

  1. ListenableFuture(可监听的Future): Futures,用于异步完成的回调。
  2. Service: 控制事件的启动和关闭,为你管理复杂的状态逻辑。

  六. Strings: 一个非常非常有用的字符串工具类: 提供 splitting,joining, padding 等操作。

  七. Primitives: 扩展 JDK 中未提供的对原生类型(如int、char等)的操作, 包括某些类型的无符号的变量。

  八. Ranges: Guava 一个强大的 API,提供 Comparable 类型的范围处理, 包括连续和离散的情况。

  九. I/O: 简化 I/O 操作, 特别是对 I/O 流和文件的操作, for Java 5 and 6.

  十. Hashing: 提供比 Object.hashCode() 更复杂的 hash 方法, 提供 Bloom filters.

  十一. EventBus: 基于发布-订阅模式的组件通信,但是不需要明确地注册在委托对象中。

  十二. Math: 优化的 math 工具类,经过完整测试。

  十三. Reflection: Guava 的 Java 反射机制工具类。

 

 

二、Guice是Google开发的一个轻量级,基于Java5(主要运用泛型与注释特性)的依赖注入框架(IOC)。Guice非常小而且快。Guice是类型安全的,它能够对构造函数,属性,方法(包含任意个参数的任意方法,而不仅仅是setter方法)进行注入。
Guice还具有一些可选的特性比如:自定义scopes,传递依赖,静态属性注入,与Spring集成和AOP联盟方法注入等。一部分人认为,Guice可以完全替代spring, 因为对于DI组件框架来说, 性能是很重要的, guice比spring快十倍左右, 另外, 也是最重要的一点, 使用spring很容易写成service locator的风格, 而用guice, 你会很自然的形成DI风格.甚至说,guice简单超轻量级的DI框架效率是spring的1.6倍,Spring使用XML使用将类与类之间的关系隔离到xml中,由容器负责注入被调用的对象,而guice将类与类之间的关系隔离到Module中,声明何处需要注入,由容器根据Module里的描述,注入被调用的对象,使用Annotation使用支持自定义Annotation标注,对于相同的接口定义的对象引用,为它们标注上不同的自定义Annotation注释,就可以达到同一个类里边的同一个接口的引用,注射给不同的实现,在Module里用标注做区分,灵活性大大增加。

 

Guice与Spring的对比
使用XML 
    Spring 使用将类与类之间的关系隔离到xml中,由容器负责注入被调用的对象,因此叫做依赖注入。
    Guice 不使用xml,将类与类之间的关系隔离到Module中,声名何处需要注入,由容器根据Module里的描述,注入被调用的对象。
使用Annotation
    Guice 使用,支持自定义Annotation标注,使用Annotation也未必是好事,范型等新特性也未必是好事,目前大多的服务器均不支持jdk1.5,wls要9以前才支持,而目前的客户由于价格原因也很少选用wls9的,至少我们做过的项目中都没有。功能再强,客户不需要,何用?
运行效率
    Spring 装载spring配置文件时,需解析xml,效率低,getBean效率也不高,不过使用环境不会涉及到getBean,只有生产环境的时候会用到getBean,在装载spring应用程序的时候,已经完成全部的注射,所以这个低效率的问题不是问题。
    Guice 使用Annotation,cglib, 效率高与spring最明显的一个区别,spring是在装载spring配置文件的时候把该注入的地方都注入完,而Guice呢,则是在使用的时候去注射,运行效率和灵活性高。
类耦合度
    Spring 耦合度低,强调类非侵入,以外部化的方式处理依赖关系,类里边是很干净的,在配置文件里做文章,对类的依赖性极低。
    Guice 高,代码级的标注,DI标记@inject侵入代码中,耦合到了类层面上来,何止侵入,简直侵略,代码耦合了过多guice的东西,大大背离了依赖注入的初衷,对于代码的可维护性,可读性均不利。
类编写时
    Spring 需要编写xml,配置Bean,配置注入。
    Guice 只需声明为@inject,等着被注入,最后在统一的Module里声明注入方式。
仅支持IOC
    Spring 否,spring目前已经涉猎很多部分。
    Guice 是,目前仅仅是个DI容器。
是否易于代码重构
    Spring 统一的xml配置入口,更改容易。
    Guice 配置工作是在Module里进行,和spring异曲同功
支持多种注入方式
    Spring 构造器,setter方法。
    Guice Field,构造器,setter方法。
灵活性
    Guice 1.如果同一个接口定义的引用需要注入不同的实现,就要编写不同的Module,烦琐 2.动态注入,如果你想注射的一个实现,你还未知呢,怎么办呢,spring是没办法,事先在配置文件里写死的,而Guice就可以做到,就是说我想注射的这个对象我还不知道注射给谁呢,是在运行时才能得到的的这个接口的实现,所以这就大大提高了依赖注射的灵活性,动态注射。
与现有框架集成度
    Spring 1.高,众多现有优秀的框架(如struts1.x等)均提供了spring的集成入口,而且spring已经不仅仅是依赖注入,包括众多方面。2. Spring也提供了对Hibernate等的集成,可大大简化开发难度。3.提供对于orm,rmi,webservice等等接口众多,体系庞大。
    Guice 1.可以与现有框架集成,不过仅仅依靠一个效率稍高的DI,就想取代spring的地位,有点难度。
配置复杂度
    Spring 在xml中定位类与类之间的关系,难度低。
    Guice 代码级定位类与类之间的关系,难度稍高。

 

 

三、Joda-Time 令时间和日期值变得易于管理、操作和理解。事实上,易于使用是 Joda 的主要设计目标。其他目标包括可扩展性、完整的特性集以及对多种日历系统的支持。并且 Joda 与 JDK 是百分之百可互操作的,因此您无需替换所有 Java 代码,只需要替换执行日期/时间计算的那部分代码。

为什么要使用 Joda?考虑创建一个用时间表示的某个随意的时刻 — 比如,2000 年 1 月 1 日 0 时 0 分。我如何创建一个用时间表示这个瞬间的 JDK 对象?使用 java.util.Date?事实上这是行不通的,因为自 JDK 1.1 之后的每个 Java 版本的 Javadoc 都声明应当使用 java.util.Calendar。Date 中不赞成使用的构造函数的数量严重限制了您创建此类对象的途径。然而,Date 确实有一个构造函数,您可以用来创建用时间表示某个瞬间的对象(除 “现在” 以外)。该方法使用距离 1970 年 1 月 1 日子时格林威治标准时间(也称为 epoch)以来的毫秒数作为一个参数,对时区进行校正。考虑到 Y2K 对软件开发企业的重要性,您可能会认为我已经记住了这个值 — 但是我没有。Date 也不过如此。那么 Calendar 又如何呢?我将使用下面的方式创建必需的实例:
Calendar calendar = Calendar.getInstance();
calendar.set(2000, Calendar.JANUARY, 1, 0, 0, 0);

使用 Joda,代码应该类似如下所示:
DateTime dateTime = new DateTime(2000, 1, 1, 0, 0, 0, 0);

这一行简单代码没有太大的区别。但是现在我将使问题稍微复杂化。假设我希望在这个日期上加上 90 天并输出结果。使用 JDK,我需要使用清单 1 中的代码:

清单 1. 以 JDK 的方式向某一个瞬间加上 90 天并输出结果

Calendar calendar = Calendar.getInstance();
calendar.set(2000, Calendar.JANUARY, 1, 0, 0, 0);
SimpleDateFormat sdf = new SimpleDateFormat("E MM/dd/yyyy HH:mm:ss.SSS");
calendar.add(Calendar.DAY_OF_MONTH, 90);
System.out.println(sdf.format(calendar.getTime()));

使用 Joda,代码如清单 2 所示:

清单 2. 以 Joda 的方式向某一个瞬间加上 90 天并输出结果

DateTime dateTime = new DateTime(2000, 1, 1, 0, 0, 0, 0);
System.out.println(dateTime.plusDays(90).toString("E MM/dd/yyyy HH:mm:ss.SSS");

两者之间的差距拉大了(Joda 用了两行代码,JDK 则是 5 行代码)。现在假设我希望输出这样一个日期:距离 Y2K 45 天之后的某天在下一个月的当前周的最后一天的日期。坦白地说,我甚至不想使用 Calendar 处理这个问题。使用 JDK 实在太痛苦了,即使是简单的日期计算,比如上面这个计算。正是多年前的这样一个时刻,我第一次领略到 Joda-Time 的强大。使用 Joda,用于计算的代码如清单 3 所示:

清单 3. 改用 Joda
    
DateTime dateTime = new DateTime(2000, 1, 1, 0, 0, 0, 0);
System.out.println(dateTime.plusDays(45).plusMonths(1).dayOfWeek()
  .withMaximumValue().toString("E MM/dd/yyyy HH:mm:ss.SSS");

清单 3 的输出为:
Sun 03/19/2000 00:00:00.000

如果您正在寻找一种易于使用的方式替代 JDK 日期处理,那么您真的应该考虑 Joda。如果不是这样的话,那么继续痛苦地使用 Calendar 完成所有日期计算吧。当您做到这一点后,您完全可以做到使用几把剪刀修建草坪并使用一把旧牙刷清洗您的汽车。Joda 和 JDK 互操作性JDK Calendar 类缺乏可用性,这一点很快就能体会到,而 Joda 弥补了这一不足。Joda 的设计者还做出了一个决定,我认为这是它取得成功的构建:JDK 互操作性。Joda 的类能够生成(但是,正如您将看到的一样,有时会采用一种比较迂回的方式)java.util.Date 的实例(和 Calendar)。这使您能够保留现有的依赖 JDK 的代码,但是又能够使用 Joda 处理复杂的日期/时间计算。例如,完成 清单 3 中的计算后。我只需要做出如清单 4 所示的更改就可以返回到 JDK 中:

清单 4. 将 Joda 计算结果插入到 JDK 对象中
    
Calendar calendar = Calendar.getInstance();
DateTime dateTime = new DateTime(2000, 1, 1, 0, 0, 0, 0);
System.out.println(dateTime.plusDays(45).plusMonths(1).dayOfWeek().withMaximumValue().toString("E MM/dd/yyyy HH:mm:ss.SSS");
calendar.setTime(dateTime.toDate());

就是这么简单。我完成了计算,但是可以继续在 JDK 对象中处理结果。这是 Joda 的一个非常棒的特性。


Joda 的关键日期/时间概念
Joda 使用以下概念,它们可以应用到任何日期/时间库:不可变性(Immutability)、瞬间性(Instant)、局部性(Partial)、年表(Chronology)、时区(Time zone)。我将针对 Joda 依次讨论每一个概念。
不可变性
    我在本文讨论的 Joda 类具有不可变性,因此它们的实例无法被修改。(不可变类的一个优点就是它们是线程安全的)。我将向您展示的用于处理日期计算的 API 方法全部返回一个对应 Joda 类的新实例,同时保持原始实例不变。当您通过一个 API 方法操作 Joda 类时,您必须捕捉该方法的返回值,因为您正在处理的实例不能被修改。您可能对这种模式很熟悉;比如,这正是 java.lang.String 的各种操作方法的工作方式。
瞬间性
    Instant 表示时间上的某个精确的时刻,使用从 epoch 开始计算的毫秒表示。这一定义与 JDK 相同,这就是为什么任何 Joda Instant 子类都可以与 JDK Date 和 Calendar 类兼容的原因。更通用一点的定义是:一个瞬间 就是指时间线上只出现一次且唯一的一个时间点,并且这种日期结构只能以一种有意义的方式出现一次。
局部性
    一个局部时间,正如我将在本文中将其称为局部时间片段一样,它指的是时间的一部分片段。瞬间性指定了与 epoch 相对的时间上的一个精确时刻,与此相反,局部时间片段指的是在时间上可以来回 “移动” 的一个时刻,这样它便可以应用于多个实例。比如,6 月 2 日 可以应用于任意一年的 6 月份(使用 Gregorian 日历)的第二天的任意瞬间。同样,11:06 p.m. 可以应用于任意一年的任意一天,并且每天只能使用一次。即使它们没有指定一个时间上的精确时刻,局部时间片段仍然是有用的。我喜欢将局部时间片段看作一个重复周期中的一点,这样的话,如果我正在考虑的日期构建可以以一种有意义的方式出现多次(即重复的),那么它就是一个局部时间。
年表
    Joda 本质 — 以及其设计核心 — 的关键就是年表(它的含义由一个同名抽象类捕捉)。从根本上讲,年表是一种日历系统 — 一种计算时间的特殊方式 — 并且是一种在其中执行日历算法的框架。受 Joda 支持的年表的例子包括:ISO(默认)、Coptic、Julian、Islamic。Joda-Time 1.6 支持 8 种年表,每一种都可以作为特定日历系统的计算引擎。
时区
    时区是值一个相对于英国格林威治的地理位置,用于计算时间。要了解事件发生的精确时间,还必须知道发生此事件的位置。任何严格的时间计算都必须涉及时区(或相对于 GMT),除非在同一个时区内发生了相对时间计算(即时这样时区也很重要,如果事件对于位于另一个时区的各方存在利益关系的话)。DateTimeZone 是 Joda 库用于封装位置概念的类。许多日期和时间计算都可以在不涉及时区的情况下完成,但是仍然需要了解 DateTimeZone 如何影响 Joda 的操作。默认时间,即从运行代码的机器的系统时钟检索到的时间,在大部分情况下被使用。

 

四、Joda 实际上是涵盖众多用于 Java 语言的替代 API 的大型项目,因此从技术上讲,使用 Joda 和 Joda-Time 名称表示相同的意思是一种误称。但在撰写本文之际,Joda-Time API 目前似乎是唯一处于活跃开发状态下的 Joda API。考虑到 Joda 大型项目的当前状态,我想将 Joda-Time 简称为 Joda 应该没什么问题。
官网还在更新中,她目前包含以下的替代API(具体我没有使用过,Google的Guava有相同的工具API):

 类似资料: