目前主要有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>