JDK 8将停止支持
Oracle JDK 8将于2019年1月(从现在起153天)停止发布公共更新,时间不多了,所以虫虫认为现在是最好的时机迁移到最新版JDK版本,享受新版本的各种黑魔法和新功能了。
另外,在虫虫以前的文章中也提到过,从JDK 11开始,其发布周期变为每6个月一个新版本,每3年发布一个新的LTS版本(下一个是JDK 11)。
值得一提的是,发布的公开使用的Oracle JDK将支持OpenJDK源代码进行编译而无需更改。
Java会收费吗?
ORACLE确保,不会对Java公共开放不会收费。所有Java/JDK开发基于OpenJDK版本库中完成,所有修复和增强功能都将推送到该开放库。
另外,三方公司也都有自己JRE实现,比如谷歌,Azul和RedHat等,以及IBM开源的Java/JDK实现OpenJ9:
向Java 11迁移
在Java 9,10和即将发布的Java 11 LTE,我们来着重介绍下如何迁移Java应用程序和模块及其可能存在的未解决的问题。虽然类似的文章问多,但是深入的太少,大多数文章都集中在简单的Hello World应用程序上。
本文我们将介绍个基于Spring Boot框架的应用程序为实例分步介绍迁移具体过程。选择这样做的示例应用程序是Spring PetClinic,一个使用WebMVC,Actuator,Cache,Data JPA,Thymeleaf和Test starters的Spring Boot 2示例应用程序。完全迁移到Java 11基本上有三个主要过程:
1.使用JDK 11运行现有Java应用程序。
2.使用Java 11编译应用程序。
3.模块化应用程序以使用模块系统。
如果你还没有准备好同时做这三件事,那请追随虫虫脚本开始。
JDK 11开发环境构建
首先,你需要为你的操作系统下载并安装JDK 11。
首先更新你喜欢的IDE以支持Java模块系统:
根据各自的IDE官网下载相应模块(地址略):
Eclipse IDE,IntelliJ IDEA,Apache NetBeans
1.使用JDK 11运行现有Java应用程序
为什么要从JDK 8升级到JDK 11?因为我们将能享受到以下这些的黑魔法:
JDK 11 黑魔法
- 继Java 8后最新LTS长期支持版本(如果你拥有商业许可证)。
- 完全支持Linux容器(包括Docker)。
- 支持G1上的并行完全垃圾收集。
- 免费应用程序类,数据共享功能。
- 免费的低耗能飞行记录仪和堆分析仪。
- 备用存储设备上的堆分配。
- 新的默认根权限证书集。
- 新的ZGC和Epsilon垃圾收集器。
- Ahead-of-time编译和GraalVM。
- 最新的HTTPS安全协议TLS 1.3。
- JShell。
- 支持”shebang”Java脚本文件! #!/bin/java
运行你的应用程序
这是一个非常简单的步骤,使用早期Java版本创建的应用程序都可以在JDK 11上运行而不会出现重大问题,除非你得依赖模块中包括JEP-320中从JDK中删除的Java EE或CORBA模块。
如果缺少类,你可能需要显式添加java.activation,java.transaction和java.xml.bind依赖项。在类文件错误的情况下,你将需要更新Java字节码增强库,如ASM,bytebuddy,javassist或cglib等。
2.使用Java 11编译应用程序
为什么要将源代码升级到Java 11?
- 局部变量类型推断(var关键字)。
- 新的本机不可修改集合API。
- 新的反应流API。
- 改进的流/谓词/可选API。
- 改进的系统过程API。
- 改进的文件API。
- 支持HTTP/2。
- 标准Java异步HTTP客户端。
- 多版本JAR。
详细步骤:
1.克隆Spring PetClinic存储库
1
|
git clone https://github.com/spring-projects/spring-petclinic.git
|
2.更新版本信息
打开pom.xml并更新java.version属性
1
2
3
|
<java.version>11</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
|
3.删除cobertura-maven-plugin引用
因为它不支持JDK 11,而且也有年头不维护了。你可以使用支持较新JDK版本的JaCoCo。
4.更新javassist和mockito-core依赖项
1
2
3
4
5
6
7
8
9
10
11
|
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.23.1–GA</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito–core</artifactId>
<version>2.20.0</version>
<scope>test</scope>
</dependency>
|
5.包括mockito-core depende
1
2
3
4
5
6
7
8
9
|
<plugin>
...
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito–core</artifactId>
<version>2.20.0</version>
</dependency>
...
</plugin>
|
2018
6.使用最新的asm依赖项将maven-compiler-plugin更新到3.7.0+版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven–compiler–plugin</artifactId>
<version>3.7.0</version>
<configuration>
<release>${java.version}</release>
</configuration>
<dependencies>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>6.2</version>
</dependency>
</dependencies>
</plugin>
|
7.使用最新的asm依赖项将maven-surefire-plugin更新到2.21.0+版本
1
2
3
4
5
6
7
8
9
10
11
|
<plugin>
<artifactId>maven–surefire–plugin</artifactId>
<version>2.21.0</version>
<dependencies>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>6.2</version>
</dependency>
</dependencies>
</plugin>
|
8.包含java.xml.bind模块依赖项,因为JEP-320在JDK 11中删除了这些模块
1
2
3
4
5
|
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.4.0-b180608.0325</version>
</dependency>
|
9.在pom.xml中包含GlassFish JAXB存储库
1
2
3
4
5
6
7
|
<repositories>
<repository>
<id>jvnet-nexus-staging</id>
<url>http://maven.java.net/content/repositories/staging/</url>
<layout>default</layout>
</repository>
</repositories>
|
10.使用spring-boot-maven-plugin运行应用程序
1
|
./mvnw spring–boot:run
|
然后可以通过浏览器访问应用程序界面:localhost:8080
11.打包应用程序并运行测试
1
|
./mvnw clean package
|
12.使用打包的可执行jar运行应用程序
1
|
java –jar target/spring–petclinic–2.0.0.BUILD–SNAPSHOT.jar
|
现在,你可以使用Java 11编译和运行应用程序,但还没用到模块系统。
3.模块化应用程序以使用模块系统
为什么要迁移到模块系统?
- 配置更可靠:用程序组件声明显式依赖的方法来替换脆弱的,容易出错的类路径机制。
- 强封装:允许组件声明其中哪些公共类型可供其他组件访问,哪些不可访问。
- 为你的应用程序创建最小的JRE映像。
- 减少应用程序内存占用量。
- 优化应用程序启动时间。
具体步骤如下:
1.创建模块文件module-info.java
在src/main/java目录中创建一个名为module-info.java的文件,其中包含以下内容:
1
2
3
|
module spring.petclinic {
}
|
现在,当你尝试编译应用程序时,你会看到如下所示很多错误告警:
Error:(19, 27) java: package org.springframework.boot is not visible
(package org.springframework.boot is declared in module spring.boot, but module spring.petclinic does not read it)
这表明应用程序已经表现为模块化布局,并且必须连接模块以进行编译和运行时。
你可以使用Maven依赖项插件解析目标列出当前在类路径中的所有模块名称,并将它们添加到module-info中:
1
|
./mvnw compile org.apache.maven.plugins:maven–dependency–plugin:3.1.1:resolve
|
注意:上面的命令不排除传递依赖项或包括JDK模块。
遗憾的是,由于许多原因,jdeps无法帮助你生成模块描述列表,但主要是因为第三方库尚未添加模块系统描述,它们被视为特殊的自动模块。
最终的模块描述符应如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
open module spring.petclinic {
requires cache.api;
requires java.activation;
requires java.instrument;
requires java.persistence;
requires java.sql;
requires java.transaction;
requires java.validation;
requires java.xml.bind;
requires org.hibernate.validator;
requires spring.beans;
requires spring.boot;
requires spring.boot.autoconfigure;
requires spring.context;
requires spring.core;
requires spring.data.commons;
requires spring.data.jpa;
requires spring.tx;
requires spring.web;
requires spring.webmvc;
requires jdk.unsupported;
}
|
注意:由于Spring Framework和Hibernate JPA的反射要求,open关键字是必需的,jdk.unsupported是sun.misc.Unsafe的survivors。
2.构建并引入modules目录
引入maven-jar-plugin以创建应用程序jar(仅限类)并将其复制到modules目录。
1
2
3
4
5
6
7
8
9
|
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<outputDirectory>
${project.build.directory}/modules
</outputDirectory>
</configuration>
</plugin>
|
3.引入maven-dependency-plugin以将运行时依赖项复制到模块目录。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven–dependency–plugin</artifactId>
<version>3.1.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>copy–dependencies</goal>
</goals>
<configuration>
<outputDirectory>
${project.build.directory}/modules
</outputDirectory>
<includeScope>runtime</includeScope>
<excludeArtifactIds>
spring–boot–devtools
</excludeArtifactIds>
</configuration>
</execution>
</executions>
</plugin>
|
注意:应该从模块中排除spring-boot-devtools依赖项。
4.包括java.persistence和java.transaction模块依赖项
它们修复了更新版本的自动模块问题:
1
2
3
4
5
6
7
8
9
10
|
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate–jpa–2.1–api</artifactId>
<version>1.0.2.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.transaction</groupId>
<artifactId>jboss–transaction–api_1.2_spec</artifactId>
<version>1.1.1.Final</version>
</dependency>
|
同样增加javax.transaction依赖给 spring-boot-starter-data-jpa.
1
2
3
4
5
6
|
<exclusions>
<exclusion>
<artifactId>javax.transaction–api</artifactId>
<groupId>javax.transaction</groupId>
</exclusion>
</exclusions>
|
注意:当存在module-info.java并且启用了fork进程时,surefire会创建一个包含模块和未命名模块的混合类路径,从而导致模块可见性问题,这会阻止应用程序启动。
6.打包并测试应用程序
1
|
./mvnw clean package
|
7.使用Java模块系统运行应用程序
1
2
3
|
java —add–opens java.base/java.lang=spring.core,javassist \
—module–path target/modules
—module spring.petclinic/org.springframework.samples.petclinic.PetClinicApplication
|
注意:由于Spring和Hibernate依赖项会有请求JDK反射访问,因此需要–add-opens。
8.设置main-class属性
使用以下命令设置模块main-class属性,删除使用module参数指定的主类:
1
2
3
|
jar —update \
—file=target/modules/spring–petclinic–2.0.0.BUILD–SNAPSHOT.jar
—main–class=org.springframework.samples.petclinic.PetClinicApplication
|
9.为了自动执行上一步,你可以添加exec-maven-plugin:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<executions>
<execution>
<id>module-main-class</id>
<phase>package</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>jar</executable>
<arguments>
<argument>
–update
</argument>
<argument>
–file=${project.build.directory}/modules/${project.build.finalName}.jar
</argument>
<argument>
–main-class=org.springframework.samples.petclinic.PetClinicApplication
</argument>
<argument>
–module-version=${project.version}
</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
|
10.现在你可以在没有显式主类声明的情况下运行应用程序:
1
2
3
4
|
./mvnw clean package
java —add–opens java.base/java.lang=spring.core,javassist
—module–path=target/modules
—module spring.petclinic
|
注意:由于MJAR-238,Maven还不支持开箱即用。
ok,整个迁移过程就完成。如果你对该文档和问题,请给虫虫留言。