Java 的参数检查与断言 - Guava Preconditions

在前一篇 Scala 的参数检查与断言: require, assert, assume 和 ensuring,捉摸 Scala 的断言时提到了 JDK 内置对断言的粗略支持,也就是 assert 语句,并且默认该特性是被关掉,需 -ea 开启。

assert object != null;
assert object != null : "object can't be null";

还进一步接触了 Scala 的 Predef 方法 require, assert, assume, 和 ensuring 是怎么检验参数与断言运算结果的,Scala 的这些方法在校验失败时相应的抛出 IllegalArgumentException 和  AssertionError 异常。

JDK  7 引入了 Objects 工具类,它的三个 T requireNotNull(T object) 方法能对参数进行 null 值检查,null 时抛出 NullPointerException

阅读全文 >>

Scala 的参数检查与断言: require, assert, assume 和 ensuring

似乎 C/C++ 的编程人员相比于 Java 更偏爱于断言,JDK 1.4 才开始引入 assert 的支持,但默认是关闭的,需要用 -ea 编译选项打开,否则代码中的 assert 语句全被忽略,一般会在单元测试中开启该选项。简单回顾一下 JDK 自带的断言,它用两种写法

assert object != null;
assert object != null : "object can't be null";

第一个参数是个 bool 值,断言失败只会笼统的抛出java.lang.AssertionError 异常,并不区分是在检验方法参数还是中间运算结果。严谨来说我们会希望参数检查不通过时抛出 java.lang.IllegalArgumentException; 而中间运算结果的断言不过希望抛出 java.lang.AssertionError, 最好是 java.lang.IllegalStateException

很多时候我们不会去使用 -ea 编译选项,也就是主动放弃了 JDK 本身的断言功能。介于两个因素(不同的断言错误和默认的断言选项关闭),Scala 为我们提供了更方便的参数检查与断言方法,它们来自于 Predef, 其所定义的方法可以直接使用

阅读全文 >>

SQL Server 和 HSQLDB 中使用 merge into 完成 saveOrUpdate 操作

当我们调用 Hibernate 的  saveOrUpdate() 或 JPA 的 save() 方法的 Hibernate 实现时,都会做两步操作:1)按 ID 查询记录是否已存在,2)不存在插入新记录,存在则更新原记录。这种两步操作其实可以在 SQL Server 和 HSQLDB 中一条语句完成,这就是本文要介绍的 merge into 语句。感觉到用数据库自己的特性,并且一条语句会比 saveOrUpdate() 两步操作性能要好,还需实测。

之所以把 SQL Server 和 HSQLDB 扯到一块来讲,是因为我们在实际项目中的单元测试是基于 HSQLDB 内存数据库的。merge into 如其名所示,它应该是给予我们便利的去根据把一个表中符合条件的记录合并到另一个表中去。我们这里只利用它的这特性去实现类似 Hibernate 的 saveOrUpdate() 操作。

假设我们有一个简单的表

如果指 id 的记录已存在更新原来记录的 name 和  address, 不存在则插入新记录 阅读全文 >>

Spring 如何初始化泛型类实例

在 Java 中对于泛型类型,比如这样简单的类定义

class Processor<T> {}

如果直接初始化时要指定具体类型的话,我们可以这么写

Processor<String> processor = new Processor<>();  //Java 7 及以上版本

Spring 对基本泛型的初始化

如果我们要用 Spring 容器来初始化这个类,比如给上面那个类加个 @Named 注解

@Named
class Processor<T> {
}

这时候我们通过 beanFactory.getBean(Processor.class) 得到的是一个什么样的实例呢?Spring 怎么知道要指定什么具体类型呢?很简单,任何不确定的情况都是 Object。所以通过容器得到的  Processor 实例相当于用下面代码构造出来的

Processor processor = new Processor();  //更准确来讲是 Processor<Object> processor = new Processor<>();

再进一步,对于有上限约束的泛型定义,Spring 才如何应对呢?像 阅读全文 >>

Spring 项目中把 SQL 语句写在 .sql 文件中

我们在使用 JDBC 时, 如果把所有的 SQL 语句全写在 Java 文件中, 由于 Java 不支持 Here Document, 多行字符串要么用加号, 要么用 Java 8 的 String.join() 方法来连接, 同时不能对 SQL 语句进行语法加亮, 所以这样的 SQL 字符串阅读性很差. 别说为何不用 Hibernate 之类的而不直接写原始的 SQL 语句, 在操作复杂的系统时还是会用到 JdbcTemplate 吧.

所以我们希望能把 SQL 语句写在单独的 *.sql 文件里, 这样很多编辑器就能语法高亮显示, 或在输入时还能得到智能提示. 有种办法是把 *.sql 用作为属性文件, 那么在其中定义多行的 SQL 语句时就得这样

select.user=select id, firstname, lastname, address \
  from users \
  where id=?

加载后就能用 getProperty("select.user") 来引用相应的语句了. 属性文件的换行与 Bash  一样, 也是用  \, 但如此, 则 *.sql 并非一个纯粹的 SQL 文件, 不能正确的进行语法加亮, 一旦写上 SQL 的注释 -- 就更是在添乱了.

所以我们的第二个方案是: 首先 *.sql 就该是一个真正的  SQL 文件, 而不是伪装的属性文件, 为了能在程序中引用每一条 SQL 语句, 我们该如何表示各自的 Key 呢? 这里的灵感仍然是来自于 Linux Shell, 在 Linux Shell 中指定执行环境的用了特殊的注释方式 #!, 如

#!/bin/bash
#!/usr/bin/env python

阅读全文 >>

Java 单元测试如何断言(检查)控制台输出

关于在 JUnit 单元测试中如何断言某个函数的控制台输出已是我一个长久的问题. 虽然有控制台输出的函数有了副作用, 不能称之为一个纯函数, 在讲求函数式编程的今天, 纯函数是最好测试的, 所谓的 Data In, Data Out. 但总还是有这样的需求, 比如自己实现的某个日志框架的 Appender, 需要验证它向控制台的输出内容.

我先前在项目中的办法是, 先把把标准输出定向到一个 ByteArrayOutputStream 中去, 完后把这个流转成字符串来断言它的内容, 最后恢复标准输出为 System.out, 代码如下:

ByteArrayOutputStream output = new ByteArrayOutputStream();
System.setOut(new PrintStream(output));

System.out.print("Hello");

assertThat(output.toString(), is("Hello");
System.setOut(System.out);

这样也能完成任务, 本质也是对的, 但稍显复杂了些. 今天读 Spring in Action 一书, 发现它用了 StandardOutputStreamLog 这个 JUnit 的 @Rule, 来自于 System Rules. 其实 StandardOutputStreamLog 类已不推荐使用, 取而代之的是 SystemOutRule, 所以应用 SystemOutRule 来断言控制台输出的测试方法就是 阅读全文 >>

Kafka 生产消费 Avro 序列化数据

本文实践了如何连接 Kafka 生产和消费 Avro 序列化格式的数据, 不能像 NgAgo-gDNA 那样, 为保证实验内容及结果的可重复性, 文中所用的各中间件和组件版本如下:

  1. Apache Kafka: kafka_2.11-0.10.0.1, 这个版本在初始始化生产者消费者的属性与之前版本有所不同.
  2. kafka-clients: Java API 客户端, 版本为  0.10.0.1
  3. Apache Avro: 1.8.1. 关于 Avro 序列化的内容可参见 Apache Avro 序列化与反序列化 (Java 实现)
  4. Java 8

Apache Kafka 消息系统设计为可以传输字符串, 二进制等数据, 但直接用于传输生产消费两端都能理解的对象数据会更友好.  所以我们这里用 Avro 的 Schema  来定义要传输的数据格式, 通信时采用自定义的序列化和反序列化类进行对象与字节数组间的转换.

以下是整个实验过程

本地启动 Apache Kafka 服务

请参考 简单搭建 Apache Kafka 分布式消息系统 启动 ZooKeeper 和 Kafka 即可. 程序运行会自动创建相应的主题. 启动后 Kafka 开启了本地的 9092 端口, 程序中只需要连接这个端口, 不用管 ZooKeeper 的  2181 端口. 阅读全文 >>

Apache Avro 序列化与反序列化 (Java 实现)

像两个人交流一样要找一个互相能理解的语言, 在国内为普通话, 跑国外多用英语相通, 两个进程间通信也需要找一个大家都能理解的数据格式. 简单的如 JSON, XML, 那是自我描述性格式, XML 有 Schema 定义, 但尚无正式的 JSON Schema 规范. 在讲求效率的场合, 纯文本式的数据交换格式无法满足要求, 于是有二进制的 Google Protobuf 和 Apache Avro. 在 Apache 的生态像 Hadoop, Kafka 中自然是选用 Avro.

Avro 支持多种语言, 如 C, C++, C#, Java, PHP, Python 和 Ruby. 它使用 JSON 来定义 Schema, 通过工具可以由 Schema 生成相应语言的数据对象, 比如 Java 的  avro-tools.jar. 这样可以在跨进程跨语言透明的实现为对象交换.

本文体验 Java 环境中 Avro 数据格式的序列化与反序列化.

Avro Schema 文件就是数据生产和消费端的通信协议; 我们可以由 Schema 生成相应的 Java 对象, 然后以具体的 Java 对象交换, 或者不生成 Java 对象而纯粹以 GenericRecord 交互. 为操作数据的简单, 我们通常采用前一种方式, 即生成具体数据传输对象. 阅读全文 >>

Giter8 -- 把项目布局模板放到 GitHub 上

因为学习或做些小 Demo, 会临时建立一个项目, 项目的布局也常有类似, 不想每次为一个 Maven 项目而执行 mkdir -p example/src/{main,test}/{java,resources}, 或是通过 IDE 来创建, 于是萌生了把自己常用的项目模板放到 GitHub 上的想法. 我们当然可以把直接在 GitHub 上创建一个个项目模板仓库, 想用时只要 git clone 下来, 但克隆的总是与 GitHub 上相应的仓库有关联.

Google 了, 有不少方法能建立项目基本框架, 如

  1. 创建 Maven 项目骨架,  mvn archetype:generate -DarchetypeGroupId=.....
  2. sbt 的 np 插件可以快速生成项目目录
  3. YEOMAN 也有自己的 Generator, 很强大也复杂

再就是现在要介绍的, 比较适合于我的口味的 Giter8, 简单实用, 定义模板更是轻松自如. Giter8 是构建在 sbt launcher 之上的用于从 GitHub 或其他任何 Git 仓库中攫取项目模板的命令行工具. 模板定义简单, 支持变量的提示输入. 下面是安装, 使用, 以及建立自己的模板: 阅读全文 >>

如何定制 Spring Boot 的 Banner

当我们启动一个 Spring Boot 的 Hello World 程序, 可以看到 Spring 会在控制台下输出一段 ASCII 字符组成的 Spring 字样, 像这个图中的样子, spring-boot-banner并且 ":: Spring Boot ::" 是绿色显示, 版本号灰色. 如果每个 Spring Boot 做的应用都用默认的 Banner 就不好玩了, 无外乎只在声明我正在用 Spring Boot. 好在 Spring 提供了多种方式让我们定制自己的 Banner.

官方文档: http://docs.spring.io/spring-boot/docs/1.3.8.RELEASE/reference/htmlsingle/#boot-features-banner

初始 Banner 的代码是 SpringApplicationBannerPrinter 类.

Spring Boot 默认寻找 Banner 的顺序是:

  1. 依次在 Classpath 下找 文件 banner.gif, banner.jpg, 和 banner.png, 先找到谁就用谁
  2. 继续 Classpath 下找 banner.txt
  3. 上面都没有找到的话, 用默认的 SpringBootBanner, 就是我们最常见到的那个

阅读全文 >>