使用 JMockit 来 mock 构造函数

Java 测试的 Mock 框架以前是用 JMockit, 最近用了一段时间的 Mockito, 除了它流畅的书写方式,经常这也 Mock 不了,那也 Mock 不了,需要迁就于测试来调整实现代码,使得实现极不优雅。比如 Mockito 在 私有方法,final 方法,静态方法,final 类,构造方法面前统统的缴械了。powermock 虽然可作 Mockito 的伴侣来突破 Mockito 本身的一些局限,但是我一用它来 Mock 一个构造方法就出错

Caused by: java.lang.ClassNotFoundException: org.mockito.exceptions.Reporter

原因是 Mockito 变化太快,powermock 跟不上它的步伐 -- https://github.com/powermock/powermock/issues/684,于是我只能止步。

不得已再祭出 JMockit 这号称(也确实是)一无所不能的大杀器,在此见识一下它怎么 Mock 构造函数的

本篇实例所使用的 JMockit 版本是 1.30, 当前最新版 1.31, 由于尚未被 Maven 中央仓库收录,所以暂用 1.30。在 pom.xml 中如下方式引入 阅读全文 >>

JdbcTemplate 易被 Java 8 Lambda 带入的坑

Spring 的 JdbcTemplate 为我们操作数据库提供非常大的便利,不需要显式的管理资源和处理异常。在我们进入到了 Java 8 后,JdbcTemplate 方法中的回调函数可以用 Lambda 表达式进行简化,而本文要说的正是这种 Lambda 简化容易给我们带来的一个 Bug, 这是我在一个实际项目中写的单元测试发现的。

下面就是我们的一个样板代码,在我们的 UserRespository 中有一个方法 findAll() 用于获得所有用户:

阅读全文 >>

Java 的参数检查与断言 - Spring Assert

之前有一篇 Java 的参数检查与断言 介绍了在 Java 中如何应用 Guava 的 Preconditions 来进行参数检查与状态断言,原本是可以心无旁骛,专心的用它就行了,可是刚刚因琢磨我们使用 JdbcTemplate 存在的一大 Bug,阅读 Spring 的源代码时发现 spring-util 也提供了一个类似于 Gruva Preconditions 的工具类 -- Assert,它是自 Spring 1.1.2 开始就静静的躺在那儿了。

所以现在要检查参数或状态断言时反而犯上了选择综合证,虽然内心还是偏向于 Guava Preconditions,但总之不那么坚决了,也不知到底是谁在重新发明着轮子。可以说 Spring Assert 与 Guava Preconditions 的功能基本一致,也是针对入口参数或中间运行结果的检查分别抛出 IllegalArgumentException 和 IllegalStateException . 下面一张图来了解它的所有方法。 阅读全文 >>

Java 可变参 Object...objects 方法的陷进

Java 的可变参数(Varargs) 方法让我们调用起来很方便,不需要总是去构造一个数组来传递不定数量的参数,而且还可以作为方法的一个可选参数,如

void foo(int id, String...name) {
    String yourName = name.length == 0 ? "Anonymous" : name[0];
        ......
}

想要告诉名字就调用 foo(1, "Yanbin"), 不想的话就用 foo(1).

但我们在使用 Java Varargs 时,当变参类型定义为 Object...objects 时就要当心了,因为 Object 类型的包容性原因一不小心就可以掉到坑里去了,例如下面的方法

void foo(Object...objects) {
     Arrays.stream(objects).forEach(System.out::println());
}

当引用类型是 Object[] 时调用没问题,下面代码调用可以得到预期的结果 阅读全文 >>

Java 与 Python 通过 Apache Avro 交换数据

最近转战到 Amazon 的云服务 AWS 上,考虑到在使用它的 Lambda 服务时 Python 应用有比较可观的启动速度,与之相比而言,Java 总是慢热型,还是一个内存大户。所以有想法 Lambda 函数用 Python 来写,来增强响应速度,而内部的应用仍然采用 Java, 于是就有了 Java 与 Python 的数据交换格式。使用 Kafka 的时候是用的 Apache Avro, 因此继续考察它。

注意,本文的内容会有很大部份与前一篇 Apache Avro 序列化与反序列化 (Java 实现) 雷同,不过再经一次的了应用,了解更深了。

在不同类型语言间进行数据交换,很容易会想到用 JSON 格式,那我们为什么还要用 Apache Avro 呢?通过接下来的内容,我们可以看到以下几点:

  1. Apache Avro 序列化的格式也是 JSON 的,Java 的 Avro 库依赖于 Jackson 库
  2. 序列化数据库本身带有 Schema 定义的,方便于反序列化,特别是对于 Java 代码; 而 JavaScript 会表示多此一举
  3. 自动支持序列化数据的压缩,在官方提供的库中,Java 可支持 deflate, snappy, bzip2, 和 xz. 其他语言中可能少些,如 Python 只支持 deflate, 和 snappy, 应该可扩充。序列化数据中 Schema 部分不被压缩
  4. 天然支持序列化对象列表,这样在序列化数据中只需要一份 Schema,类似于数据库表 Schema 加上多记录行的表示方式。只用 Apache Avro 传输小对象的话,数据量比 JSON 事 JDK 序列化的数据要大。

Apache Avro 官方提供有 C, C++, C#, Java, PHP, Python 和 Ruby 的支持库,可在网上找到其他语种的类库,如 NodeJS, Go 的,等等。 阅读全文 >>

WordPress 在 Linux 下不能发送邮件的问题

本博客迁移到现在的 VPS 上已是两年有余,在有评论到来时从来就没收到过提醒邮件,以前是放在租的主机上,所以那些都有人配置好了的。其实一直没把 WordPress 未能发送邮件的问题放心上,自己收不到倒无所谓,但评论者接收邮件提醒算是件重要的事。

今天一同事说我的博客无法进行评论,我一段时间以来只有一丝感觉为何这么久没人评论了,只想大家都不爱说话罢了,并未意思到博客本身的问题,也在此表示感谢。于是自己测试了下,登陆用户评论没问题,游客无法评论,最后查到是 wpDiscuzMath Comment Spam Protection 插件起冲突了,于是把后者禁用了就没问题。至于评论提交后响应有些慢是由于不得不用的 Akismet 插件的原因。

为评论的问题既然登陆到了 VPS 上了,也顺便把两年来的邮件发送问题也诊断一下,其实做来很简单,就是当初配置 VPS 时少做了一步。当前所用的 Linux 发行版是  Debian,之前记录过一篇 Debian Linux VPS 为 WordPress 的系统准备,其中提到 WordPress 发送邮件用的是 exim4,

所以第一步,修改 /etc/exim4/update-exim4.conf.conf, dc_eximconfig_configtype 的值由 'local' 改成 'internet'

dc_eximconfig_configtype='internet'

然后重启 exim4 服务,可用命令 service exim4 restart. 阅读全文 >>

一个简单的 Java 自动批处理队列

实际中可能有这样的应用场景,得到一个记录不需要立即去处理它,而是等累积到一定数量时再批量处理它们。我们可以用一个计数器,来一个加一个,量大时一块处理,然后又重零开始计数。如果记录的来源单一还好办,要是有多个数据源来提供记录就会有多线程环境下数据丢失的问题。

这里我编写了一个最简单的任务批处理的队列,构造了告诉它批处理数量,消费者,然后就只管往队列里添加记录,队列在满足条件时自动进行批处理。因为内部使用的是 BlockingQuque 来存储记录,所以多线程往里同时添加记录也没关系,最后的未达到 batchSize, 的那些记录可主动调用 completeAll() 函数或在达到 timeout 后来触发批处理,并且结束队列内的循环线程。

注意: 多线程环境下往一个无线程保护的集合或结构中,如 ArrayList, LinkedList, HashMap, StringBuilder 中添加记录非常容易造成数据的丢失,而往有线程保护的目的地写东西就安全了,如 Vector, Hashtable, StringBuffer, BlockingQueue。当然性能上要付出一点代价,不过对于使用了可重入锁(ReentrantLock), 而非同步锁(synchronized) 的数据结构还是可以放心使用的。

下面是 BatchQueue 的简单实现 阅读全文 >>

Java8 Lambda 表达式与 Checked Exception

当我们在使用 Java 8 的 Lambda 表达式时,表达式内容需要抛出异常,也许还会想当然的让当前方法再往外抛来解决编译问题,如下面的代码

main() 方法抛出 Exception 还是不解决决编译错误,仍然提示 "Unhandled exception: java.io.FileNotFoundException"。

因为我们可能保持着惯性思维,忽略了 Lambda 本身就是一个功能性接口方法的实现,所以把上面的代码还原为匿名类的方式

那么对于上面那种情况应该如何处理呢? 阅读全文 >>

Java 运行时如何获取泛型参数的类型

在 Java 中对于下面最简单的泛型类

class A<T> {
    public void foo() {
        //如何在此处获得运行时 T 的具体类型呢?
    }
}

设想我们使用时

new A<String>().foo();

是否能在 foo() 方法中获得当前的类型是 String 呢?答案是否定的,不能。在 foo() 方法中 this 引用给不出类型信息,this.getClass() 就更不可能了,因为 Java 的泛型不等同于 C++ 的模板类,this.getClass() 实例例是被所有的不同具体类型的 A 实例(new A<String>(), new A<Integer>() 等) 共享的,所以在字节码中类型会被擦除到上限。

我们可以在 IDE 的调试时看到这个泛型类的签名

阅读全文 >>

《Practical Vim》阅读笔记 (2)

1. Vim 的模式: 通常我们说有三种模式,正常模式(Normal Mode), 插入模式(Inert Mode) 和可视模式(Visual Mode). 其实除此之外还有
    4) Operator-Pending Mode: 就是正常模式下按下操作(Operator) 指令(如 c, d 等) 后,等待输入移动(Motion) 指示时的模式。这时 Escape 或 Motion 指令后退回到正常模式
    5) Insert Normal Mode: 在插入模式时,按下 <Ctrl-o> 组合键后进入该模式, 此时可接受一个 Action (Operator + Motion = Action), 比如 3dd, 然后自动返回到插入模式。<Esc> 也能从 Insert Normal Mode 返回到插入模式。这方便了插入模式只想执行一次指令操作时来回切换, Insert Normal Mode 在 Vim 的状态栏中显示为下图那样 - NORMAL --(insert) --

    6) 替换模式(Replace Mode)-- REPLACE --R 会进到替换模式,随后输入往后覆盖,可尝试 <Insert> 键;  r{char} 和 gr{char} 临时进入单字符的替换模式,替换完当前字符立即退到正常模式,也就是光标不往下走。
    7) 可视替换模式(Visual Replace Mode) -- VREPLACE --, 用 gR 进入该模式,在处理 tab 字符时会更友好,还有别的好处吧,所以该书建议尽量用可视替换模式。
    8) 可视模式(Visual Mode),这就复杂了。它有三个子模式:
      character-wise Visual mode --  -- VISUAL ;
      line-wise Visual mode --  -- VISUAL LINE --
      block-wise Visual mode -- -- VISUAL BLOCK --

    9) 选择模式(Select Mode),可视模式下选择了内容后用 <C-g> 可在两种模式下切换,状态栏 -- VISUAL -- 和 -- SLECT -- 的不同,还有选择模式下直接输入就会替换当前的内容,与其他编辑器的行为一样,而可视模式需要按 c 来替换当前内容。 阅读全文 >>