簡體   English   中英

Eclipse / Idea忽略了Maven Java版本配置

[英]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)。
編譯會很好,但你會在運行時遇到錯誤(即NoClassDefFoundErrorNoSuchMethodError ,可能也是更通用的LinkageError )。

使用source / target ,Java編譯器可以用作交叉編譯器,以生成可在JDK上運行的類文件,這些文件實現了早期版本的Java SE規范。

人們普遍認為使用兩個編譯器選項就足夠了。 但是, source選項指定我們正在編譯的版本,而target選項指定要支持的最低Java版本。

編譯器用於字節碼生成, sourcetarget用於在交叉編譯期間生成兼容的字節碼。 但是,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 / targetbootclasspath選項,我們仍可能在交叉編譯期間遺漏一些內容,正如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 / targetbootclasspath靈活性。 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.0maven-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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM