[英]Maven Java Version Configuration ignored by Eclipse/Idea
我有:
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
然而我宣布:
public enum DirectoryWatchService {
INSTANCE;
private java.util.Optional<String> test;
private java.nio.file.Files files;
}
Eclipse沒有打擾。 IntelliJ也不是。 即使是Maven也不會打擾。 我甚至可以做一個mvn清潔包 。 在沒有任何警告的情況下構建糟糕的東西。
您正在遇到交叉編譯對source
/ target
選項的誤解。 在類路徑中使用主要版本(JDK 7或8)但希望針對次要版本進行編譯(在您的情況下為6)。
編譯會很好,但你會在運行時遇到錯誤(即NoClassDefFoundError
或NoSuchMethodError
,可能也是更通用的LinkageError
)。
使用source
/ target
,Java編譯器可以用作交叉編譯器,以生成可在JDK上運行的類文件,這些文件實現了早期版本的Java SE規范。
人們普遍認為使用兩個編譯器選項就足夠了。 但是, source
選項指定我們正在編譯的版本,而target
選項指定要支持的最低Java版本。
編譯器用於字節碼生成, source
和target
用於在交叉編譯期間生成兼容的字節碼。 但是,Java API不由編譯器處理(它們是作為JDK安裝的一部分提供的,着名的rt.jar
文件)。 編譯器沒有任何API知識,只是編譯當前的rt.jar
。 因此,當使用JDK 1.7編譯target=1.6
,編譯器仍將指向JDK 7 rt.jar
。
那么,我們怎樣才能真正實現正確的交叉編譯?
從JDK 7開始,如果source
/ target
不與bootclasspath
選項結合使用,javac會發出警告 。 在這些情況下, bootclasspath
選項是指向所需目標Java版本的rt.jar
的關鍵選項(因此,您需要在計算機中安裝目標JDK)。 因此, javac
將有效地編譯好的Java API。
但這可能仍然不夠!
並非所有Java API都來自rt.jar
。 lib\\ext
文件夾提供其他類。 另外一個javac
選項用於extdirs
。 來自官方Oracle文檔
如果要進行交叉編譯(針對不同Java平台實現的bootstrap和擴展類編譯類),則此選項指定包含擴展類的目錄。
因此,即使使用source
/ target
和bootclasspath
選項,我們仍可能在交叉編譯期間遺漏一些內容,正如javac文檔附帶的官方示例中所述。
Java平台JDK的javac默認也會針對自己的引導類進行編譯,因此我們需要告訴javac來編譯JDK 1.5引導類。 我們使用-bootclasspath和-extdirs執行此操作。 如果不這樣做,可能允許針對1.5平台上不存在的Java Platform API進行編譯,並且會在運行時失敗。
但是......它可能仍然不夠!
來自Oracle官方文檔
即使為交叉編譯設置了適當的bootclasspath和-source / -target,編譯器內部契約(例如匿名內部類的編譯方式)也可能不同,例如JDK 1.4.2中的javac和JDK 6中的javac使用-target 1.4選項運行。
建議的解決方案(來自Oracle官方文檔)
生成可在特定JDK上運行的類文件的最可靠方法是使用最舊的JDK編譯源文件。 除此之外,必須設置bootclasspath以便對較舊的JDK進行可靠的交叉編譯。
那么,這真的不可能嗎?
Spring 4目前支持Java 6,7和8 。 甚至使用Java 7和Java 8功能和API。 那怎么能兼容Java 7和8 ?!
Spring使用source
/ target
和bootclasspath
靈活性。 Spring 4總是使用source
/ target
編譯到Java 6,因此字節碼仍然可以在JRE 6下運行。因此,沒有使用Java 7/8語言功能:語法保持Java 6級別。
但它也使用Java 7和Java 8 API! 因此,不使用bootclasspath
選項。 使用可選,Stream和許多其他Java 8 API。 只有在運行時檢測到JRE 7/8時,才會根據Java 7或Java 8 API注入bean:智能方法!
但是Spring如何確保API的兼容性呢?
使用Maven Animal Sniffer插件。
此插件檢查您的應用程序是否與指定的Java版本API兼容。 被稱為動物嗅探器,因為Sun傳統上將不同版本的Java命名為不同的動物 (Java 4 = Merlin(鳥),Java 5 = Tiger,Java 6 = Mustang(馬),Java 7 = Dolphin,Java 8 =無動物)。
您可以將以下內容添加到POM文件中:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
<version>1.14</version>
<configuration>
<signature>
<groupId>org.codehaus.mojo.signature</groupId>
<artifactId>java16</artifactId>
<version>1.0</version>
</signature>
</configuration>
<executions>
<execution>
<id>ensure-java-1.6-class-library</id>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
一旦您使用JDK 7 API,同時希望僅使用JDK 6 API,構建將失敗。
在maven-compiler-plugin
的官方source
/ target
頁面中也建議使用此用法
注意 :僅僅設置
target
選項並不能保證您的代碼實際上在具有指定版本的JRE上運行。 缺陷是無意中使用的API,這些API僅存在於以后的JRE中,這會使您的代碼在運行時因鏈接錯誤而失敗。 要避免此問題,您可以配置編譯器的引導類路徑以匹配目標JRE,也可以使用Animal Sniffer Maven插件驗證代碼是否使用非預期的API。
Java 9更新
在Java 9中,這種機制從根本上改為以下方法:
javac --release N ...
將在語義上等同於
javac -source N -target N -bootclasspath rtN.jar ...
有關
javac
可用的早期版本的API的信息
- 以壓縮方式存儲
- 僅提供平台中立的Java SE N和JDK N -exported API
- 與
-source
/-target
相同的發布值N.- 拒絕不兼容的選項組合
--release N
方法的主要優點:
- 沒有用戶需要管理存儲舊API信息的工件
- 應該刪除需要使用像Maven插件Animal Sniffer這樣的工具
在舊版本中可能使用比
javac
更新的編譯習語
- Bug修復
- 速度提升
Java 9和Maven上的更新
從版本3.6.0
, maven-compiler-plugin
通過其release
選項提供對Java 9的支持:
自Java9以來支持Java編譯器的
-release
參數
一個例子:
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<release>8</release>
</configuration>
</plugin>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.