目前主要有Oracle JDK,OpenJDK和其余一些企业编译的JDK。html
简单地说,OpenJDK是OracleJDK的开源版本。java
如下OpenJDK的介绍来自维基百科:mysql
OpenJDK原是Sun Microsystems公司为Java平台构建的Java开发环境(JDK)的开源版本,彻底自由,开放源码。Sun Microsystems公司在2006年的JavaOne大会上称将对Java开放源代码,于2009年4月15日正式发布OpenJDK。甲骨文在2010年收购Sun Microsystem以后接管了这个项目。 历史: 2008年5月,Fedora 9及Ubuntu 8.04于发行版中发布OpenJDK,完整地基于自由及开放源代码的OpenJDK。 2008年6月,IcedTea 6(Fedora 9上的一个包版本的OpenJDK)宣布已经过Technology Compatibility Kit测试,能够称得上是一个彻底兼容的Java 6的运行环境。 2008年7月12日,Debian接受了OpenJDK-6的不稳定版本,但当前状况已经稳定。OpenJDK也能够在openSUSE、Red Hat Enterprise Linux及其派生系统,如CentOS中找到。 自2008年7月,OpenJDK 7能够运行在Mac OS X和其余的BSD发行版。 2009年7月,Ubuntu 9.04中的二进制版本OpenJDK在Java SE 6 JCK中经过了全部的兼容性测试。 2016年8月22日,Google在Android 7.0 Nougat中,将专利的JDK替换成开源方案的OpenJDK,以完全解决Java的专利问题。
Oracle JDK以前被称为SUN JDK,甲骨文收购SUN以后更名为Oracle JDK。实际上,Oracle JDK也是基于OpenJDK源代码构建的,所以Oracle JDK和OpenJDK之间并无重大的技术差别,基本上能够认为性能、功能和执行逻辑上二者是一致的。linux
二者的不一样之处主要有:spring
关于OpenJDK,还有一些事情须要了解。sql
目前Java的开发是以OpenJDK项目的形式进行的,而OpenJDK项目是彻底开源的(许可证是 GPLv2+CE,就是说你能够根据OpenJDK的源码开发本身的JDK,可是你的JDK也得要开源),该项目目前由Oracle主导,汇聚了社区的力量进行开发,IBM,红帽等企业都有参与。在OpenJDK项目的官网上你能够看到OpenJDK的源码(包括JVM)与提交记录等。apache
该项目只提供源码,并无提供各个操做平台(linux,windows,mac等)上的JDK binaries。bootstrap
Oracle's OpenJDK与AdoptOpenJDK都是对OpenJDK项目的源码进行build以后的产物,即JDK binaries。这二者只要版本相同就是同样的,且二者均提供了不一样操做系统(linux,windows,mac等)的binaries。但AdoptOpenJDK会提供更长(可能长达数年)的更新服务,而Oracle's OpenJDK只提供六个月的更新服务。所以建议使用AdoptOpenJDK,而不是Oracle's OpenJDK。windows
另外,Oracle's OpenJDK不是Oracle JDK。Oracle JDK是Oracle提供的付费版本的JDK,和Oracle's OpenJDK几乎没有区别,但Oracle JDK提供长时间的支持服务、安全更新等。api
只要不在生产环境下使用Oracle JDK或其余商业版JDK,JAVA就仍然能够无偿使用。
事实上也能够直接对OpenJDK的源码进行build,来生成本身的JDK binaries,而后本身追踪OpenJDK的源码更新,本身打安全补丁。
对于那些对安全性稳定性要求很高,同时比较富裕的企业来讲,也不必定要使用Oracle JDK。其余的许多公司,好比阿里巴巴,亚马逊,IBM等,也提供了本身版本的JDK binaries,他们或者也免费,或者有着不一样的收费、更新周期等,能够根据企业本身的实际须要来进行选择。
例如Amazon Corretto,也是一个基于OpenJDK编译的,采用GPL+CE协议开源的JDK。
从2017年9月发布Java 9开始,Oracle每六个月就会发布一个新版本的JDK(具体来讲是每一年的三月和九月),每三年会有一个LTS(Long Term Support release,长期支持)版本。Java 8 与 Java 11 为当前提供支持的LTS版本。下一个LTS版本应该是Java 17。
下图来自wiki-Java版本历史:https://zh.wikipedia.org/zh-cn/Java%E7%89%88%E6%9C%AC%E6%AD%B7%E5%8F%B2
Oracle JDK对LTS版本提供三年支持,而社区的AdoptOpenJDK承诺对LTS版本提供至少4年的支持。
咱们建议的策略是:
AdoptOpenJDK下载网址:https://adoptopenjdk.net/
这个地址从国内访问太慢了,能够从清华大学提供的镜像网站下载:
国内镜像:https://mirrors.tuna.tsinghua.edu.cn/AdoptOpenJDK/
这里下载的是OpenJDK11U-jdk_x64_linux_hotspot_11.0.7_10.tar.gz
。
AdoptOpenJDK目前能够选择的JVM有两种。一种是原有的hotspot,另外一种是IBM开源的openj9。
目前一个简单的评价是,openj9占用的内存更少,但CPU密集任务方面不如hotspot。
就目前而言,通常开发与生产环境仍是推荐hotspot,其理论、JVM参数、命令行工具及性能调优的资料更丰富一些。
若是想切换到openj9,须要对其JVM的相关理论、参数、工具进行一些必要的调研。
找个目录存放下载好的openJDK11,如:/usr/java
;
解压缩:
tar -zxvf OpenJDK11U-jdk_x64_linux_hotspot_11.0.7_10.tar.gz
若是你想安装多个版本的JDK,下面这一步骤只在指定系统默认JDK版本时须要执行。
而后设置环境变量(编辑/etc/profile
):
export JAVVA_HOME=/usr/java/jdk-11.0.7+10 export CLASSPATH=.:${JAVA_HOME}/lib export PATH=${JAVA_HOME}/bin:$PATH
而后执行source /etc/profile
,并确认java版本:java -version
。
如下内容参考了文章:https://zhuanlan.zhihu.com/p/87157172
。
固然,Java版本升级是有风险的。例如从Java8升级到Java11的话,由于从Java9开始,JDK中去除了一些模块,如JAXB和JAX-WS的相关依赖,对于这些去除的模块,若是你的工程中用到了,那么你须要单独下载这些依赖包。
IBM提供了一个检测工具,叫Migration Toolkit for Application Binaries
,能够扫描应用程序二进制文件(.ear 或 .war 文件)并生成一份报告,突出显示在应用程序中发现的潜在的 Java 11 问题。地址:https://developer.ibm.com/wasdev/downloads/#asset/tools-Migration_Toolkit_for_Application_Binaries
。有兴趣的同窗能够自行下载学习。
下面是具体的一些事项:
无效的目标发行版 11
的现象实际上是maven工具或IDE中工程的jdk版本没有选择JDK11而致使的。)因为在Java 9 以后,每六个月版本会升级一次,若是你依赖的库有处理Java字节码相关的库,应该注意下对应版本的升级。例如:
你能够根据项目工程实际状况,选择添加独立依赖,或者在依赖没有冲突的状况下,把下面全部依赖都添加上。
<dependency> <groupId>com.sun.activation</groupId> <artifactId>javax.activation</artifactId> <version>1.2.0</version> </dependency>
<dependency> <groupId>javax.transaction</groupId> <artifactId>javax.transaction-api</artifactId> <version>1.2</version> </dependency>
<dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.2.8</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-core</artifactId> <version>2.2.8</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId> <version>2.2.8</version> </dependency> <dependency> <groupId>com.sun.xml.ws</groupId> <artifactId>jaxws-ri</artifactId> <version>2.3.0</version> <type>pom</type> </dependency>
<dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.1</version> </dependency>
Java9引入了模块化,Java Platform Module System,java平台模块系统,简称JPMS。JPMS提供了一个模块化平台,使用来自Java语言规范的访问控制概念来强制实施类型可访问性封装。每一个模块都定义了哪些包将被导出,从而可供其余模块访问。一个包能够包含一个 API。若是模块中的包未导出,则表示该模块的开发人员不但愿模块外部使用这些包的API。若是外部仍然使用这些包,则会抛出错误!
对于这种错误,最好固然是更换其余API。若是难以实现,则能够经过添加编译以及启动参数解决。
--add-exports
:模块声明中的exports语句将模块中的包导出到全部或其余模块,所以这些模块可使用该包中的公共API。 若是程序包未由模块导出,则可使用--add-exports
的命令行选项导出程序包:--add-exports <source-module>/<package>=<target-module-list>
,若是设置target-module-list为ALL-UNNAMED,那么全部Classpath下的module,均可以访问source-module中的pakage包下的公共API。--add-opens
:模块声明中的opens语句使模块里面的包对其余模块开放,所以这些模块能够在运行期使用深层反射访问该程序包中的全部成员类型。 若是一个模块的包未打开,可使用--add-opens命令行选项打开它。 其语法以下:--add-opens <source-module>/<package>=<target-module-list>
,若是设置target-module-list为ALL-UNNAMED,那么全部Classpath下的module,均可以访问source-module中的pakage包下的全部成员类型。在编译阶段(javac),只须要添加--add-exports
,对于执行阶段(java),最好把--add-exports
和--add-opens
都加上。同时,为了明确全部须要添加的模块和包,能够经过添加--illegal-access=${value}
来检查。这个value能够填写:
能够经过设置--illegal-access=deny
来明确须要添加的全部--add-export
和--add-open
包。
经过JDK内置的jdeps工具查找过时以及废弃API以及对应的替换:
jdeps --jdk-internals -R --class-path 'libs/*' $project
其中,libs是你的全部依赖的目录,$project是你的项目jar包,示例输出:
JDK Internal API Suggested Replacement ---------------- --------------------- sun.misc.BASE64Encoder Use java.util.Base64 @since 1.8 sun.reflect.Reflection Use java.lang.StackWalker @since 9
一些在JDK11过时,可是JDK8使用的API:
Java 8的ClassLoader流程:
java9及以后的classloader流程:
同时,JDK9开始,AppClassLoader的父类再也不是 URLClassLoader了。这致使使用了AppClassLoader
的热部署或插件部署,如Spring-Boot的热部署,会报异常:
Exception in thread "main" java.lang.ClassCastException: java.base/jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to java.base/java.net.URLClassLoader at org.springframework.boot.devtools.restart.DefaultRestartInitializer.getUrls(DefaultRestartInitializer.java:93) at org.springframework.boot.devtools.restart.DefaultRestartInitializer.getInitialUrls(DefaultRestartInitializer.java:56) at org.springframework.boot.devtools.restart.Restarter.<init>(Restarter.java:140) at org.springframework.boot.devtools.restart.Restarter.initialize(Restarter.java:546) at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationStartingEvent(RestartApplicationListener.java:67) at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationEvent(RestartApplicationListener.java:45) at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:122) at org.springframework.boot.context.event.EventPublishingRunListener.starting(EventPublishingRunListener.java:69) at org.springframework.boot.SpringApplicationRunListeners.starting(SpringApplicationRunListeners.java:48) at org.springframework.boot.SpringApplication.run(SpringApplication.java:292) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118) at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107) at com.asofdate.AsofdateMain.main(AsofdateMain.java:18)
对于动态加载的类,咱们在OpenJDK11中只能自定义类加载器去加载,而不是经过获取APPClassLoader去加载。同时,这么作也有助于你随时能将动态加载的类卸载,由于并无加载到APPClassLoader。
建议使用自定义的类加载器继承java.security.SecureClassLoader
去加载类。
若是你想访问classpath下的内容,你能够读取环境变量:
String pathSeparator = System.getProperty("path.separator"); String[] classPathEntries = System.getProperty("java.class.path").split(pathSeparator);
Java8到Java11有不少JVM参数变化。总的来讲能够总结为两类参数的变化:一是GC相关的,让GC配置调优更加简单;二是日志相关的,日志统一到了一块儿,不像以前那么混乱。
https://docs.oracle.com/javase/9/tools/java.htm
,搜索The following sections describe the options that are obsolete, deprecated, and removed
;https://docs.oracle.com/javase/10/tools/java.htm
,搜索The following sections describe the options that are obsolete, deprecated, and removed
;https://docs.oracle.com/en/java/javase/11/tools/java.html
,搜索The following sections describe the options that are obsolete, deprecated, and removed
;每次的版本变化都须要关注如下三个部分:
这些不推荐的,过期的或移除的参数,若是有替代参数,请使用替代参数。
Java8升级到Java11后,lombok的版本须要升级到1.18
以上。
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.10</version> </dependency>
在Java8升级到Java11的过程当中,咱们的本地编译环境每每同时存在Java8与Java11两套环境。
这里说明一下在本地同时安装有不一样版本JDK的时候,如何让Maven同时支持两种Java版本的编译。
假定你已经在本地安装了maven 3.5,在其conf/settings.xml
中,添加新的profile配置:
<profiles> <!-- 原来的profile --> <profile> <id>jdk-1.8</id> <activation> <!-- 默认激活 --> <activeByDefault>true</activeByDefault> <jdk>1.8</jdk> </activation> ... </profile> <!-- 为JDK11添加的profile --> <profile> <id>openJDK11</id> <activation> <jdk>11</jdk> </activation> <properties> <JAVA_HOME>/usr/java/jdk-11.0.7+10</JAVA_HOME> <JAVA_VERSION>11</JAVA_VERSION> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <maven.compiler.compilerVersion>11</maven.compiler.compilerVersion> </properties> <repositories> <repository> <id>xxx-Repository</id> <name>xxx Maven Repository</name> <url>maven私服地址</url> <snapshots> <enabled>true</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>xxx-Repository</id> <name>xxx Maven Repository</name> <url>maven私服地址</url> <snapshots> <enabled>true</enabled> </snapshots> </pluginRepository> </pluginRepositories> </profile> </profiles>
若是你是在IDEA中直接用maven插件编译,那么:
1.首先确认项目相关JDK版本是否都已经指定为JDK11:
2.确认IDEA使用了本地maven,且对应的java版本是JDK11:
3.确认Maven插件使用的profile是前面新添加的openJDK11
:
此时,你就可使用上图中的Lifecycle
下的各类maven命令了。
同时你能够看到,maven-compiler-plugin的版本是3.1,并不须要升级到3.8:
若是你本地的环境变量JAVA_HOME
仍然是以前的版本,好比jdk1.8,可是你又想直接在命令行使用mvn命令对工程进行编译,那么你须要在命令行会话窗口先临时将JAVA_HOME
改成JDK11,而后再执行mvn命令:
# 先cd到目标目录 cd <工程目录> # 检查mvn当前使用的java版本与Java home mvn -version # 改写JAVA_HOME到jdk11安装目录 export JAVA_HOME=/usr/java/jdk-11.0.7+10 # 从新检查mvn当前使用的java版本与Java home mvn -version # 确认java home已是JDK11之后,执行mvn命令 mvn clean install package
放心,在当前会话改写JAVA_HOME并不会致使系统默认环境变量被改写,仅对当前会话生效。
对于springMVC,springboot,springcloud的工程,Java8升级到Java11后,对应的spring版本也须要升级。
如下版本升级均采用maven工具
根据Spring官方文档的描述,Spring Framework 5.1.x 或 Spring Boot 2.1.x 开始才支持JDK11。
从https://spring.io/projects/spring-framework#learn
选择某个版本的Reference Doc.
,而后选择章节overview
。
或直接访问地址:https://docs.spring.io/spring/docs/<版本号>/spring-framework-reference/overview.html#overview
,注意把<版本号>
替换为具体的版本号如5.0.17.RELEASE
或5.1.15.RELEASE
。
由此可知,jdk11须要Spring Framework 5.1以上。
从https://spring.io/projects/spring-boot#learn
选择某个版本的Reference Doc.
,而后选择章节9. System Requirements
。
对于历史版本,能够直接访问地址https://docs.spring.io/spring-boot/docs/<版本号>/reference/html/getting-started-system-requirements.html
,注意把<版本号>
替换为具体的版本号如2.1.10.RELEASE
或2.0.8.RELEASE
。
由此可知,jdk11须要Springboot 2.1以上。
打开https://spring.io/projects/spring-cloud#overview
,向下翻到Table 1. Release train Spring Boot compatibility
:
由此可知,Springcloud的Greenwich SR5
与springboot的2.1.x
是对应的。
从https://spring.io/projects/spring-cloud#learn
中选择某个版本的Reference Doc
(这里我选择的是Greenwich SR5
),点开选择Single HTML
,而后在文档中,查找jdk相关章节,发现JDK 11 Support
中有以下说明:
即,jdk11后,springcloud的Eureka注册中心须要单独引入jaxb-runtime
的依赖。
对于springboot或springcloud项目来讲,为了解决依赖包的版本管理和版本冲突问题,通常不会直接在dependency中显式地写version,而是省略掉version,经过下面两种方法,让spring的版本项目帮咱们作版本管理。
一种是默认方式,经过parent标签引入spring-boot-starter-parent
:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.14.RELEASE</version> </parent>
另外一种方式是这里推荐的写法,采用dependencyManagement标签,引入spring的版本兼容的依赖管理项目(经常使用的是spring-boot-dependencies
与spring-cloud-dependencies
):
<dependencyManagement> <dependencies> <dependency> <!-- Import dependency management from Spring Boot --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.1.14.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> <dependencies> <dependency> <!-- Import dependency management from Spring Cloud --> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Greenwich.SR2</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
注意
对于前面的章节[3.2 字节码处理相关依赖包的版本升级],[3.3 Java9之后移除模块的依赖对应]和[3.8 Lombok编译异常],若是对应的依赖包已经写进了pom的dependencies,那么Java8升级到Java11以后,你能够先肯定经过spring-boot-starter-parent
或 spring-boot-dependencies
与spring-cloud-dependencies
所自动管理的依赖包版本是否已经包含了上述章节的依赖包,对应的版本是否已是支持JDK11的版本。
若是版本是unknown
,说明spring-boot-starter-parent
或 spring-boot-dependencies
与spring-cloud-dependencies
中没有对应的依赖包版本管理,须要显式地写出version;若是有具体的版本号,则说明已经该依赖包已经集成到spring的依赖包版本管理中,你只须要确认该版本是否支持JDK11便可。
例如,pom以下:
... <properties> <spring-boot.version>2.1.10.RELEASE</spring-boot.version> <spring-cloud.version>Greenwich.SR2</spring-cloud.version> ... </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> ... </dependencies> </dependencyManagement> <dependencies> ... <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> ... </dependencies>
对应的lombok版本:
8.0.18
后,对应驱动class变为com.mysql.cj.jdbc.Driver
。固然,你能够选择不升级mysql-connector-java的版本,好比保持在5.1.47
版本,也是能够的。此时driver-class-name
仍然是com.mysql.jdbc.Driver
。
WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by org.apache.ibatis.reflection.Reflector (file:/home/maven_repo/org/mybatis/mybatis/3.4.6/mybatis-3.4.6.jar) to method java.lang.Integer.getChars(int,int,byte[]) WARNING: Please consider reporting this to the maintainers of org.apache.ibatis.reflection.Reflector WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release
将mybatis-spring-boot-starter
升级到新版本便可解决
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.2</version> </dependency>