Java 和 Scala 调用变参的方式

Java 和 Scala 都支持变参方法, 写在最后的位置上,最基本的调用方式也都是一样的,一个个罗列过去。也可以传入数组参数,因为变参本质上就是一个数组,就是把 ... 开始位置到最后一个参数都收纳到数组中去,所以变参之所以要放在最后的位置上,且一个方法中最多只能有一个变参类型。

这里主要是对比 Scala 对变参方法的调用,基本调用法当然是没问题的,但是在传入数组作为变参的参数列表与 Java 相对时就稍有变化了。

另外提一下,如果想传入 List 作为变参列表,而不是整体作为变参的第一个元素就是调用集合的 toArray() 方法转换成一个数组传入。

下面看 Java 中对变参方法的调用,参数列表和数组

阅读全文 >>

为 Jackson 自定义序列化对象的 JSON 格式

伴随着 Play1, 我们原来使用的 JSON 库是 Gson. 回忆下 Gson 是怎么自定义序列化对象的 JSON 格式,大概是这样子的

GsonBuilder()..registerTypeHierarchyAdapter(Cat.class, new Cat());

然后 Cat 需要实现 JsonSerializer 的 serialize() 方法。

来到了 Play2 中,JSON 库变成了 Jackson,那么 Jackson 该如何为对象自定义 JSON 格式呢?

例如,默认时 Jackson 对 Map 类型输出的是一个 JSON 对象

Map("key1"->"value1", "key2"->"value2")     转换成 JSON 是 {"key1":"value1", "key2":"value2"}

当为适应某些客户端,对于 LinkedHashMap 类型,我们想要输出的是一个有序的 JSON 数组: [{"key1":"value1"},{"key2":"value2"}]

我们就应该自定义某些 Map 的序列化格式,实现方法有两种,addSerializer 和 @JsonSerialize,不管哪种方式都需事先具体化 JsonSerializer 类,并实现它的 serialize 抽象方法

所以我先来实现一个能序列化 Map 的 JsonArrayMapSerializer 类 阅读全文 >>

sbt 中自定义的 Task

sbt, 又是一种自动化构建工具,意为 Simple Build Tool,目前还难副其名,它管理依赖也是用的 Ivy。Scala 相关的项目一般会用它,如 Play2,所以需要来研究下它怎么自定义任务。

sbt 项目可以用 build.sbt 或 project/Build.scala 来定义项目,build.sbt 里写些简单的 settings 表达式,而 Build.scala 就强大,可以写 val, object 和方法定义。而且单个 Build.scala 可以定义多个项目,build.sbt 只用来定义当前项目。build.sbt 和 project/Build.scala 能同时存在,它们的内容会编译到一块。还有一个全局的 build.sbt 文件 ~/.sbt/build.sbt,这里控制所有的项目。

所以我们分别看在 project/Build.scala 和 build.sbt 中如何定义自己的 task

首先看 project/Build.scala,分别由两行完成

val mytask = taskKey[Unit]("This is my customized task")
mytask := { println("Execute customized task")}

完整的一个 project/Build.scala 如下 阅读全文 >>

logback.xml 给变量指定默认值

随着通用日志组件转入 Slf4j,logback 也变成了默认的日志实现,像 log4j 一样,logback.xml 中也可以使用系统属性或环境变量,如 ${catalina.home}。在 log4j.properties 中,如果变量在系统属性和环境变量中找不到的话默认为 "" 空字符串,而到了 logback.xml 中如果某个变量找不到默认就是 "变量名_IS_UNDEFINED" 了,这样就比较奇怪了。

那如何在没有配置 catalina.home 系统属性或环境变量时设置一个默认值呢,例如,没有 catalina.home  时取值为 ".",这时值日志文件的路径就是

./logs/unmi-%d{yyyy-MM-dd}.log

了,也就是生成中当前目录下的 logs 子目录中,这样算是很友好的方式。下面就来分析怎么一步步找到答案的,没耐心或是只求结果的话,直接滚屏到下面就行。

我们的问题是,对于下面那样的 logback.xml 配置: 阅读全文 >>

Play2 中使用自定义的路由器文件 routes

用过 PlayFramework 的都知道默认的路由器文件是 conf/routes,Play2 可以定义自己的 routes 文件。在默认的 application.conf 中有这么一段注释

# Router
# ~~~~~
# Define the Router object to use for this application.
# This router will be looked up first when the application is starting up,
# so make sure this is the entry point.
# Furthermore, it's assumed your route file is named properly.
# So for an application router like conf/my.application.Router,
# you may need to define a router file my.application.routes.
# Default to Routes in the root package (and conf/routes)
# application.router=my.application.Routes

也就是通过 application.router 可以定义自己的 routes 文件。上面的解释很容易把人搞混,问题在于何处是文件路径,何处是类路径,至少写着的 'conf/my.applicaton.Router 就是在混淆视听。对于上面的解释要明白下面几点 阅读全文 >>

Play1 直接调用 Action 方法,不作 302 跳转

用过 PlayFramework 的同学们应该都知道,Action 方法间的调用是进行的  302 重定向操作。

简单例子说明一下,当基于下面的 r1, r2 路由配置时,如果 Application.f1() 方法中调用了 f2() 方法,实际运作是 f1() 在调用 f2() 时,会先反向出 f2() 方法对应的路由  GET /r2,然后向 /r2 发出的一个 302 跳转.

上面也算是绕个弯形成了对 f2() 方法的调用,这也是非常合理,在 Action 中很容易理解的。

GET     /r1                                                                     Application.f1
GET     /r2                                                                    Application.f2
GET     /r3                                                                    Application.f3

为什么说会反向出 f2() 方法对应的路由,可以反证一下。

例如说在 f1() 中调用了一个 public static void f4() 方法,但是 f4() 并未出现在 routes 配置中,也就是 f4() 没有对应的路由配置,我们将会看到这样一个异常 阅读全文 >>

Java 设计之初为何就不让用 == 比较两字符串呢?

Java 的字符串值比较不能用 == 号这个设计不知道最初是怎么考虑的,它最大的贡献无疑是滋生了一个长久未衰的面试题,加之连 Code Review 都可能被忽略掉的 Bug。本来两个字符串用等号相比较是最自然而然的做法,然而它却是要迫使我们相信想当然很可能是错误的那样一个道理。

我一直认为 Java 的字符串比较值不能用 == 而必须用 equals() 方法是个不恰当的设计,这从其他种种语言的现实做法(人家都用 == 比较值)就知道。

猜想一下 Java 为何要这样对待字符串,可能 Java 又想类型全部对象化,同时考虑到方便性,仍然保留了 int, short, boolean 等原始类型,它们是可以用 == 比较值,其他真正的对象类型用 equals() 方法比较也是无可厚非的。这时候夹缝中的字符串却被为难到了,它那么的常用,还常以字面量的面目出现,它更该是个基本类型,而实为对象类型,因此不被认可用 == 直接比较值, 而选择了用 equals() 方法来比较字符串值。

而另一方面,由于字符串是多例的,所以有些情况下又更令人迷惑,比如下面的种种情况 阅读全文 >>

JMockit 如何 mock 异常

2014-07-26 修改本文

后来发现用 JMockit 来 mock 异常根本没有之前文中描述的那么复杂,其实还是在那个 result 上,给它赋个异常实例就轻而易举的解决了,只需如此

原文可不用看下去了。


做过几篇 JMockit 使用 Expectations 来 Mock 方法,私有方法,私有属性的的日志,今天工作上突然有个需求是要 Mock 异常。现在再也不能为了跑个单元测试而去拔下网线了,也不该人为的去制造其他混乱来测试。开始是想能不能用 Expectations 来 Mock 异常,尚未发现相关的属性可以设置,没有类似 result 那样的属性,比如想像中有个 exception/throwable 属性: 阅读全文 >>

初始化 Gradle 工程目录

最近重新在 Eclipse 中打开旧的 Maven 项目,总有些什么错误,备受折磨。期间试手了 Ant+Ivy, 现今试用了下 Gradle,感觉不错,它应该才是我真想要的,Maven 差不多该扔到一边去了。

Gradle 像 sbt 构建工具一样,也不会帮我们生成默认的目录布局,这些要自己来做。这不是什么麻烦事,因为只需做一次,可预先写好初始化脚本,幸好本人已习惯 shell 操作。

假设我们有这样的 build.gradle 文件

apply plugin: 'java'
apply plugin: 'war'    //有这行时我们需要创建 src/main/webapp 目录
apply plugin: 'eclipse'

我们执行 gradle eclipse 后是不会生成src 目录的,工程下的源文件目录是需要自己来生成的。Gradle 参考 Maven 的标准目录布局,即

我们有两种办法初始化目录布局 阅读全文 >>

基于 Groovy 的自动化构建工具 Gradle 入门

本人工作之初没有使用自动化构建,后来敏捷了,开始使用 Ant - 完全面向过程的定义步骤,不进行依赖管理。再发展到 Maven,面向对象的方式管理工程,有了依赖的管理,JAR 包统一从中央仓库获得,保存在本地库。同时产生了 Ivy + Ant,让 Ant 也能管理依赖,不过 JAR 包依赖仓库也是用 Maven 的。

前面都是用 XML 来配置的,虽说 XML 的标签描述性也很强,但阅读性还是有些牵强,并且 Maven 和 Ant+Ivy 都需要 IDE 的支持,比如在 Eclipse 中有专门 Maven Dependenciesivy.xml [*] 中列出项目的依赖包,这里常会出现些问题。

而我今天想尝试使用的是基于 Groovy 的 Gradle,只要一个 build.gradle 文件,实质是一个 groovy 文件,由于可以 DSL,所以 Gradle 比 XML 更写意,更主要的是 Gradle 项目不需要 IDE 的特别支持,见后面的图

先要安装 Gradle,在我的 Mac 系统下我是用 brew install gradle 安装的,不同平台下可能用 yum, apt-get,安装后执行 gradle 命令验证。

接着按以下步骤:

1. 创建项目目录 testgradle,并进入到新创建的目录

mkdir testgradle
cd testgradle 阅读全文 >>