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 中如下方式引入 Read More
Spring 的 JdbcTemplate 为我们操作数据库提供非常大的便利,不需要显式的管理资源和处理异常。在我们进入到了 Java 8 后,JdbcTemplate 方法中的回调函数可以用 Lambda 表达式进行简化,而本文要说的正是这种 Lambda 简化容易给我们带来的一个 Bug, 这是我在一个实际项目中写的单元测试发现的。
下面就是我们的一个样板代码,在我们的UserRespository中有一个方法 findAll() 用于获得所有用户:1public List<User> findAll() { 2 List<User> users = new ArrayList<>(); 3 jdbcTemplate.query("select id, name from user", rs -> { 4 while (rs.next()) { 5 users.add(new User(rs.getInt("id"), rs.getString("name"))); 6 } 7 }); 8 return users; 9}
Read More之前有一篇 Java 的参数检查与断言 介绍了在 Java 中如何应用 Guava 的 Preconditions 来进行参数检查与状态断言,原本是可以心无旁骛,专心的用它就行了,可是刚刚因琢磨我们使用 JdbcTemplate 存在的一大 Bug,阅读 Spring 的源代码时发现 spring-util 也提供了一个类似于 Gruva Preconditions 的工具类 -- Assert,它是自 Spring 1.1.2 开始就静静的躺在那儿了。
所以现在要检查参数或状态断言时反而犯上了选择综合证,虽然内心还是偏向于 Guava Preconditions,但总之不那么坚决了,也不知到底是谁在重新发明着轮子。可以说 Spring Assert 与 Guava Preconditions 的功能基本一致,也是针对入口参数或中间运行结果的检查分别抛出
IllegalArgumentException和IllegalStateException. 下面一张图来了解它的所有方法。 Read MoreJava 的可变参数(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[]时调用没问题,下面代码调用可以得到预期的结果 Read More- 最近转战到 Amazon 的云服务 AWS 上,考虑到在使用它的 Lambda 服务时 Python 应用有比较可观的启动速度,与之相比而言,Java 总是慢热型,还是一个内存大户。所以有想法 Lambda 函数用 Python 来写,来增强响应速度,而内部的应用仍然采用 Java, 于是就有了 Java 与 Python 的数据交换格式。使用 Kafka 的时候是用的 Apache Avro, 因此继续考察它。
注意,本文的内容会有很大部份与前一篇 Apache Avro 序列化与反序列化 (Java 实现) 雷同,不过再经一次的了应用,了解更深了。
在不同类型语言间进行数据交换,很容易会想到用 JSON 格式,那我们为什么还要用 Apache Avro 呢?通过接下来的内容,我们可以看到以下几点:- Apache Avro 序列化的格式也是 JSON 的,Java 的 Avro 库依赖于 Jackson 库
- 序列化数据库本身带有 Schema 定义的,方便于反序列化,特别是对于 Java 代码; 而 JavaScript 会表示多此一举
- 自动支持序列化数据的压缩,在官方提供的库中,Java 可支持
deflate,snappy,bzip2, 和xz. 其他语言中可能少些,如 Python 只支持deflate, 和snappy, 应该可扩充。序列化数据中 Schema 部分不被压缩 - 天然支持序列化对象列表,这样在序列化数据中只需要一份 Schema,类似于数据库表 Schema 加上多记录行的表示方式。只用 Apache Avro 传输小对象的话,数据量比 JSON 事 JDK 序列化的数据要大。
Apache Avro 官方提供有 C, C++, C#, Java, PHP, Python 和 Ruby 的支持库,可在网上找到其他语种的类库,如 NodeJS, Go 的,等等。 Read More 本博客迁移到现在的 VPS 上已是两年有余,在有评论到来时从来就没收到过提醒邮件,以前是放在租的主机上,所以那些都有人配置好了的。其实一直没把 Wordpress 未能发送邮件的问题放心上,自己收不到倒无所谓,但评论者接收邮件提醒算是件重要的事。
今天一同事说我的博客无法进行评论,我一段时间以来只有一丝感觉为何这么久没人评论了,只想大家都不爱说话罢了,并未意思到博客本身的问题,也在此表示感谢。于是自己测试了下,登陆用户评论没问题,游客无法评论,最后查到是
wpDiscuz与Math 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. Read More- 实际中可能有这样的应用场景,得到一个记录不需要立即去处理它,而是等累积到一定数量时再批量处理它们。我们可以用一个计数器,来一个加一个,量大时一块处理,然后又重零开始计数。如果记录的来源单一还好办,要是有多个数据源来提供记录就会有多线程环境下数据丢失的问题。
这里我编写了一个最简单的任务批处理的队列,构造了告诉它批处理数量,消费者,然后就只管往队列里添加记录,队列在满足条件时自动进行批处理。因为内部使用的是BlockingQuque来存储记录,所以多线程往里同时添加记录也没关系,最后的未达到batchSize, 的那些记录可主动调用completeAll()函数或在达到 timeout 后来触发批处理,并且结束队列内的循环线程。
注意: 多线程环境下往一个无线程保护的集合或结构中,如 ArrayList, LinkedList, HashMap, StringBuilder 中添加记录非常容易造成数据的丢失,而往有线程保护的目的地写东西就安全了,如 Vector, Hashtable, StringBuffer, BlockingQueue。当然性能上要付出一点代价,不过对于使用了可重入锁(ReentrantLock), 而非同步锁(synchronized) 的数据结构还是可以放心使用的。
下面是 BatchQueue 的简单实现 Read More
当我们在使用 Java 8 的 Lambda 表达式时,表达式内容需要抛出异常,也许还会想当然的让当前方法再往外抛来解决编译问题,如下面的代码
让 main()方法抛出Exception还是不解决决编译错误,仍然提示 "Unhandled exception: java.io.FileNotFoundException"。
因为我们可能保持着惯性思维,忽略了 Lambda 本身就是一个功能性接口方法的实现,所以把上面的代码还原为匿名类的方式1public void foo() { 2 Stream.of("a", "b").forEach(new Consumer<String>() { 3 @Override 4 public void accept(String s) { 5 new FileInputStream(s).close(); 6 } 7});
那么对于上面那种情况应该如何处理呢? Read More- 在 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 的调试时看到这个泛型类的签名
Read More 
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来替换当前内容。 Read More