Java 9 线程栈遍历 API

什么是线程栈

继续纠缠 Java 9 的新特性,仍然是一个边角料,即 Java 9 增加了对线程栈遍历的 API。那么什么是线程栈,JVM 在创建每一个线程的同时都会创建一个私有的虚拟机栈,每一桢代表着一个方法调用,每次方法的调用与退出意味着压栈与出栈。每一桢上有局部变量,操作数常量引用等信息,这也是为什么局部变量是能最快被销毁的对象。过深的栈(比如过多的递归调用) 会出现我们程序员赖以生存的 StackOverflow。

浅显些说,线程栈就是通常我们捕获到异常后,用 e.printStackTrace() 看到自 main 方法追溯到当前方法的调用。例如:

java.lang.RuntimeException: stack
    at cc.unmi.TestStackWalking.m2(TestStackWalking.java:15)
    at cc.unmi.TestStackWalking.m1(TestStackWalking.java:10)
    at cc.unmi.TestStackWalking.main(TestStackWalking.java:6)

调用层次是 main() 调用 m1(), m1() 调用 m2(), m2() 中的代码如下

上面输出的每一行就是一个栈桢,输出了当前类名,方法名,代码行号。 阅读全文 >>

对 Java 9 把单个下划线作为关键字的猜想

我们知道 Java 的合法命名是以字母或下划线开头的字符串,当然,以前单个下划线 _ 也是一个合法的变量命名。但是自 Java 8 的第一个版本开始,单个下划线的变量名编译时会有警告

int _ = 99;

用 Java 8 编译时提示警告:

Test.java:2: warning: '_' used as an identifier
    int _ = 99;
         ^
    (use of '_' as an identifier might not be supported in releases after Java SE 8)
1 warning

这正是为 Java 9 作的预谋,以一个 LTS 版的 Java 8 作为过渡。因此,来到了 Java 9 后,单个下划线不再担当普通变量名的角色,变成了一个保留关键字,只说是另有任用。

先来看下 Java 9 中的单个下划线变量名编译时的错误提示

Test.java:2: error: as of release 9, '_' is a keyword, and may not be used as an identifier
    int _ = 99;
         ^
1 error

到底单下划线会作为什么用途呢? 已发布的 Java 10 没有给出答案,正在演进中的 Java 11 也没有相关的信息。 阅读全文 >>

Java 9 - 平台日志 API

关于 Java 9 的新特性从某本书的最后一个说起:平台日志 API。个人没感觉这个有什么实质的用途,所谓的平台日志是指 JDK 自身代码,或者是 JVM 组件中的日志输出,而在自己应用程序代码中却不会去用这个平台日志 API。这个所谓的 Platform Logging API 名称的意义也就是在这里,平台用的,在诊断时用来观察 JDK 类或 JVM 中的日志输出,比如应该可以截获到  JVM 本地代码实现中的日志输出。对我们在项目中如何处理日志并不会有什么影响,该怎么还是怎么,不过了解多一点东西应该不会浪费脑容量的。

新加的平台日志体现在 java.lang.System 中新加的几个方法和类

我们可以尝试着在代码使用一下它

输出如下

May 26, 2018 10:56:51 AM cc.unmi.TestLogging main
INFO: Hello Java 9 Platform Logging API

阅读全文 >>

该如何从 Java 8 升级到 Java 10

Java 9 出来了很久,买的书《Java 9 Revealed - For Earyly Adoption and Migration》,说怎么迁移到 Java 9,可是突然间 Java 9 就无法通过正常渠道从 Oracle 官网下载了,这书还让不让人看。当然要看,因为尽管 Java 10 出来了,但实际的变化全压在 Java 9 这个版本上的,就当是通过 Java 10 来学习 Java 9 吧。

本文随便说说 Java 8 之后的版本变迁,不涉及 Java 9 或是 Java 10 的具体新特性,并不能回答标题中的问题。作为一个不甘落后的 IT 从业人员,总是希望能紧跟技术(某一狭小特定领域的技术)的步伐。譬如说当从 Java 1.4 升级到 Java 1.5 之后,对范型也是跃跃欲试,无奈当时公司追求的是稳定压倒一切,不在服务器上升级 JDK,所以只能创造条件也要上。于是弄了个 Retrotranslator让你用JDK1.5的特性写出的代码能在JVM1.4中运行 来适配。

后来的公司,也就是现在更为激进一些,来了 JDK 6,跟; JDK 7,跟; JDK 8, 继续跟; JDK 9 发布后,不跟了。不是不想跟,而是 Java 9 实在是变化有点大,模块化带来的不仅仅语言方面的改变,而是影响到如何组织,发布应用,这也就是为什么 jigsaw 雪藏多年的缘故。其实也可以对模块化不欲理会,但是单纯的把 Java 8 换成 Java 9 造成原来的项目不能正常构建的概率也比以往要高。

Java 8 是 2014 年 3 月发布,四后半后的 Java 9 在 2017 年 9 月发布,然而 2018 年 3 月 Java 10 就出来了。谁说 Oracle 在收购 Java 后就对它不作为了呢?Oracle 在 Java 9 之后开始了 6 个月的发布周期(见 Oracle Java SE Support Roadmap),这让人如何受得了,Oracle 自己也是。更何况我们都没来得急品尝 Java 9 的滋味,Oracle 自己就把 Java 9 的生命周期给结束了,进到 Java 9 的下载而面 Java SE 9 Downloads, 自动会导向到 Java SE 10 的下载页面,想要下载 Java SE 9 的话,只能到历史存档中去找 Java SE 9 Archive Downloads阅读全文 >>

使用 PostgreSQL 的 uuid 字段类型

上一篇 使用 SQL Server 的 uniqueidentifier 字段类型 了解了 SQL Server 中如何使用 uniqueidentifier 字段类型后,现在来看下 PostgreSQL 中如何使用 uuid 字段类型。在 PostgreSQL 的字段类型是 uuid 了,所以创建一个带有 uuid 字段的表是

CREATE TABLE customers (
    id uuid PRIMARY KEY,
    name VARCHAR(36)
);

我们这里设置 id 字段类型为 uuid, 并且它是一个主键。也可以应用函数指定它的默认值,下面将会讲述到。

然后用 SQL 语句来向该表插入记录 阅读全文 >>

使用 SQL Server 的 uniqueidentifier 字段类型

SQL Server 自 2008 版起引入了 uniqueidentifier 字段,它存储的是一个 UUID, 或者叫 GUID,内部存储为 16 个字节。SQL Server 可用两个函数来生成 uniqueidentifier, 分别是 NEWID() 和 NEWSEQUENTIALID(), 后者只能用作字段的默认值。Java 也有一个 UUID 工具类 java.uti.UUID, UUID.randomUUID().toString() 生成一个随机的 UUID 字符串,在 java.util.UUID 也是用两个 long 字段表示内部状态。

SQL Server 的 uniqueidentifier 类型字段表明了内部如何存储,在我们操作它时,它的外在表现形式都是一个固定格式 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 的字符串,不区分大小写的。

本文所使用的 SQL Server 是 2017 版,通过 Docker 来启动的

docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=yourStrong(!)Password' -p 1433:1433 -d microsoft/mssql-server-linux:2017-latest

然后我们创建一个带有 uniqueidentifier 类型字段的表 阅读全文 >>

Mockito 也能 Mock final 类和 final 方法了

以实际 Java 项目中的单元测试 Mock 框架基本是 Mockito 2 了,因为它有一个十分流畅的 API。Mockito  2也为 JUnit 5 配上了 MockitoExtension, 所以 JUnit 5 下使用 Mockito 2 的关节也打通了。但在我们享受 Mockito 2 便利的同时,与 JMockit 相比局限性就很明显,因为 Mockito 2 是通过创建匿名子类来进行 Mock 的,所以任何子类对父类无法突破的方面它都存在。譬如,final 类, final 方法, 私有方法, 静态方法, 构造函数都是无法通过子类型进行重写的。所以除非特别需要,在 Mockito 2 无法胜任时都求助于 JMockit,JMockit 借助于 javaagent 取得了 JVM 的高控制权才得已为所欲为。

当 Mockito 来到了 2.1.0 版本,它也觉得不能对以上所有的限制置若罔闻, 首先带给我们的突破是它也可以 Mock final 类和 final 方法,虽然仍处于孵化器中,但毕竟是应用在单元测试中,能用就很不错了,只要以后不被拿走就行。这是官方对它的介绍 Mock the unmockable: opt-in mocking of final classes/methods

下面我亲自操作一遍,并给出更全方位的测试样例 阅读全文 >>

JUnit 5 使用 Mockito 2 进行测试

JUnit 5 刚出来那时,也就是第一个版本 5.0.0 时,还不能很好的支持 Mockito 的测试,因为 Mockito 没能跟得那么紧密。那时候 JUnit 5 只能试验性的提供了一个极不正式的 com.example.mockito.MockitoExtension, 看那包名就知道不是来真的,所以决定再等。JUnit 5 不再原生支持 JUnit 4 的 Rule,一切都将是 Extension,那也是要求 Mockito 能够与之俱进。现在等来了,JUnit 5 进化到了 5.2.0, Mockito 也早已有了一个单独的模块 mockito-junit-jupiter 来迎接它。

在 Mockito 2.1.0 的 What's new in Mockito 2 中记述了 JUnit 5 为 Mockito 2 开发了一个 MockitoExtension。追溯到 Mockito 2 的 Release Notes, 我们发现 Mockito 2 官方最早引入 MockitoExtension 的版本是 2.16.3(2018-03023)。我对 Mockito 对 JUnit 5 支持的最新更新是从这个 Pull Request MockitoExtension for JUnit5 得知的。

一句话讲就是现在的 Mockito 2 有原生态的 MockitoExtension 来支援 JUnit 5, 可以非常放心可靠的让 JUnit 5 和 Mockito 2 一起稳定工作。因此前面那个包名带 example 字样的 MockitoExtension 链接也就无效了。 阅读全文 >>

AWS S3 应用 KMS Key 进行服务端数据加密

当我们把数据搬上云端,为了保护敏感数据一定要对数据存储进行加密。而对于 S3 上的数据加密最简单莫过于启用服务端数据加密,可以是 AES-256 或 AWS-KMS。进行了服务端加密码的文件有什么不同呢?首先从 AWS S3 文件列表视图中看到的文件大小与原始文件大小是一样的,其实数据在 AWS 机房是加密存储了的,比如说直接在 AWS 服务器上拷出那些 S3 文件是不能理解的。

本文涉及的内容包括:

  1. 服务器端加密后的 S3 文件有何不同
  2. 不启用 Bucket 的默认加密如何进行服务端数据加密(含 AWS CLI 和  Java 代码操作)
  3. 启用及应用 Bucket 的默认加密
  4. 上传与下载文件的 IAM role 需要的权限

服务器端加密后的 S3 文件有何不同

首先看现象,后面才能确认我们的操作是否对数据真的进行了服务端加密。查看使用 AWS-KMS key 加密后的 S3 文件的 Overview, 看到下面两个属性

Server-side encryption
AWS-KMS
KMS key ID
arn:aws:kms:us-east-1:123456789012:key/e4ce859b-786b-4b8e-8b73-4968adb6e4ed

没加密的话, Server-side encryption 是 None, 并且没有 KMS key ID 属性。 阅读全文 >>

Springfox 解决在单一资源操作多个方法实现时生成 Swagger 文档的问题

在命名本文的标题都敲打了几分钟时间,问题很简单,然而用简短的一个标题完全描述出来却有点费事。在 Spring MVC 项目结合 Springfox 来生成 Swagger API 文档时,如果一个资源操作因为请求参数的不同而映射到多个 controller 方法,那么 Swagger 可能只能生成某一个 API 条目,其余都被忽略。至于为什么说是 "可能", 可能正好未遵循命名规范而躲过了这一劫。由此引出

我们的问题

我们这里用了资源操作一词,它包含了两部分信息: 资源与操作,比如 /users/{userId} 是资源,而发生在其上的 HTTP 各种方法,如 POST, GET, PUT, DELETE 等就是操作。而 Spring MVC 中允许我们针对不同的查询参数把相同的资源操作映射到不同的 controller 方法上,也是为了保持逻辑上更为清晰。

比如下面的例子路由配置的例子

GET /users/{userId}     UserController.getUserInfo                                    //默认
GET /users/{userId}     UserController.getUserInfo                                   //当有 ?source=file 时
GET /users/{userId}     CloudUserController.getUserInfoFromCloud       //当有 ?source=cloud 时

看到上面资源与操作完全相同,仅仅因为 source 查询参数的不同而映射到三个 controller 方法。用代码体现如下图 阅读全文 >>