Eclipse / Idea忽略了Maven Java版本配置 [英] Maven Java Version Configuration ignored by Eclipse/Idea

查看:1129
本文介绍了Eclipse / Idea忽略了Maven Java版本配置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有:

<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>

然而,我没有问题声明:

Yet I have no problem declaring:

public enum DirectoryWatchService {

    INSTANCE;

    private java.util.Optional<String> test;
    private java.nio.file.Files files;
}

Eclipse不打扰。 IntelliJ都没有。即使Maven也不打扰。
我甚至可以做一个 mvn清理包。建立一个没有任何警告的东西。

Eclipse doesn't bother. IntelliJ neither. Even Maven does not bother. I can even do a mvn clean package. Builds the darn thing without any warning whatsoever.

推荐答案

你正在碰到来源的交叉编译误解 / 目标选项。在您的类路径(JDK 7或8)中使用主要版本,但希望根据小版本进行编译(在您的情况下为6)。

编译将会很好,但您将在运行时(即 NoClassDefFoundError NoSuchMethodError ,可能也是更通用的 LinkageError )。

You are hitting the cross compilation misunderstanding of source/target options. Using a major version in your classpath (JDK 7 or 8) but wishing to compile against a minor version (6 in your case).
Compilation will be fine, but the you will have errors at runtime (i.e. NoClassDefFoundError or NoSuchMethodError, probably also the more generic LinkageError).

使用 来源 / target ,Java编译器可以用作交叉编译器来生成可在JDK上运行的类文件,实现较早版本的Java SE规范。

Using source/target, the Java compiler can be used as a cross-compiler to produce class files runnable on JDKs implementing an earlier version of the Java SE specification.

通常的看法是使用两个编译器选项就足够了。但是, source 选项指定正在编译的版本,而目标选项指定要支持的最低Java版本。

The common belief is that using two compiler options would be enough. However, The source option specifies against which version we are compiling while the target option specifies the lowest Java version to support.

编译器用于生成字节码,目标用于在交叉编译期间生成兼容的字节码。但是,Java API不由编译器处理(它们作为JDK安装的一部分提供,着名的 rt.jar 文件)。编译器没有任何API的知识,它只是针对当前的 rt.jar 进行编译。因此,当使用JDK 1.7编译时,编译器仍然会指向JDK 7 rt.jar

The compiler works on bytecode generation and source and target are used to generate compatible bytecode during cross-compilation. However, Java API are not handled by the compiler (they are provided as part of the JDK installation, the famous rt.jar file). The compiler doesn’t have any knowledge of API, it just compiles against the current rt.jar. Hence, when compiling with target=1.6 using JDK 1.7, the compiler will still point to the JDK 7 rt.jar.

那么,我们如何才能真正地进行交叉编译?

由于JDK 7,javac在的情况下打印警告来源 / target 不与 bootclasspath 选项结合使用。在这些情况下, bootclasspath 选项是关键选项,指向所需目标Java版本的 rt.jar 因此,您需要在目标机器中安装目标JDK)。因此, javac 将有效地针对好的Java API进行编译。

Since JDK 7, javac prints a warning in case of source/target not in combination with the bootclasspath option. The bootclasspath option is the key option in these cases to point to the rt.jar of the desired target Java version (hence, you need to have the target JDK installed in your machine). As such, javac will effectively compile against the good Java API.

但是这可能还不是足够!

并不是所有的Java API都来自 rt.jar 。其他课程由 lib\ext 文件夹提供。 javac 选项用于 extdirs 。从官方的Oracle文档

Not all Java API comes from the rt.jar. Other classes are provided by the lib\ext folder. A further javac option is used for that, extdirs. From official Oracle docs


如果您是交叉编译(针对不同Java平台实现的引导和扩展类编译类),则此选项指定包含扩展类的目录。

If you are cross-compiling (compiling classes against bootstrap and extension classes of a different Java platform implementation), this option specifies the directories that contain the extension classes.

因此,即使使用 source / 目标 bootclasspath 选项,我们可能会错过交叉编译期间的一些,也在官方示例它随附的javac文档。

Hence, even using source/target and bootclasspath options, we may still miss something during cross-compilation, as also explained in the official example which comes with the javac documentation.


Java Platform JDK的javac也将默认编译为自己的引导类,所以我们需要告诉javac对JDK进行编译1.5引导类代替。我们用-bootclasspath和-extdirs这样做。否则可能允许编译针对1.5虚拟机上不存在的Java Platform API,并在运行时失败。

The Java Platform JDK's javac would also by default compile against its own bootstrap classes, so we need to tell javac to compile against JDK 1.5 bootstrap classes instead. We do this with -bootclasspath and -extdirs. Failing to do this might allow compilation against a Java Platform API that would not be present on a 1.5 VM and would fail at runtime.

但可能还不够!

从Oracle官方文档


即使在bootclasspath和-source / -target都适用于交叉编译,编译器内部契约(如匿名内部类的编译方式)可能会有所不同,例如JDK 1.4.2中的javac和使用-target 1.4选项运行的JDK 6中的javac。

Even when the bootclasspath and -source/-target are all set appropriately for cross-compilation, compiler-internal contracts, such as how anonymous inner classes are compiled, may differ between, say, javac in JDK 1.4.2 and javac in JDK 6 running with the -target 1.4 option.

解决方案建议(从 Oracle官方文档)

The solution suggested (from Oracle official documentation)


最可靠的方式来生成可用于特定的类文件JDK和更高版本是使用感兴趣的最旧的JDK来编译源文件。除此之外,必须将bootclasspath设置为用于强大的交叉编译到较旧的JDK。

The most reliably way to produce class files that will work on a particular JDK and later is to compile the source files using the oldest JDK of interest. Barring that, the bootclasspath must be set for robust cross-compilation to an older JDK.

所以,这是不可能的吗?

Spring 4目前支持Java 6,7和8 。甚至使用Java 7和Java 8功能和API。如何与Java 7和8兼容?

Spring 4 currently supports Java 6, 7 and 8. Even using Java 7 and Java 8 features and API. How can it then be compatible to Java 7 and 8?!

Spring使用源代码 / 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。然后根据Java 7或Java 8 API注入Bean,只有在运行时检测到JRE 7/8时:智能方法!

Spring makes use of source/target and bootclasspath flexibility. Spring 4 always compiles with source/target to Java 6 so that bytecode can still run under JRE 6. Hence no Java 7/8 language features are used: syntax keeps Java 6 level.
But it also uses Java 7 and Java 8 API! Hence, bootclasspath option is not used. Optional, Stream and many other Java 8 API are used. It then injects beans depending on Java 7 or Java 8 API only when JRE 7/8 are detected at runtime: smart approach!

但是如何确保API兼容性?

使用 Maven动物嗅探器插件。

此插件会检查您的应用程序是否与指定的Java版本兼容。被称为动物嗅探器,因为Sun传统上在不同的动物(Java 4 = Merlin(bird),Java 5 = Tiger,Java 6 =野马(马),Java 7 =海豚,Java 8 =无动物)

Using the Maven Animal Sniffer plugin.
This plugin checks whether your application is API compatible with a specified Java version. Is called animal sniffer because Sun traditionally named the different versions of Java after different animals (Java 4 = Merlin (bird), Java 5 = Tiger, Java 6 = Mustang (horse), Java 7 = Dolphin, Java 8 = no animal).

您可以将以下内容添加到POM文件中:

You can add the following to your POM file:

<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 6 API,构建就会失败,只要使用JDK 7 API 。

And the build will fail as soon as you use JDK 7 API while wishing to use only JDK 6 API.

这个用法也推荐在官方的 source / target maven-compiler-plugin

This usage is also recommended in the official source/target page of the maven-compiler-plugin


注意:仅设置目标选项不保证您的代码实际上在具有指定版本的JRE上运行。缺陷是无意中使用仅存在于稍后JRE中的API,这将使您的代码在运行时出现链接错误。为避免此问题,您可以配置编译器的引导类路径以匹配目标JRE,也可以使用动物嗅探器Maven插件来验证代码是否使用无意义的API。

Note: Merely setting the target option does not guarantee that your code actually runs on a JRE with the specified version. The pitfall is unintended usage of APIs that only exist in later JREs which would make your code fail at runtime with a linkage error. To avoid this issue, you can either configure the compiler's boot classpath to match the target JRE or use the Animal Sniffer Maven Plugin to verify your code doesn't use unintended APIs.






Java 9更新

在Java 9中,这种机制已经彻底改变了到以下方法:

javac --release N ...

在语义上相当于

javac -source N -target N -bootclasspath rtN.jar ...





  • 早期版本可用于 javac


  • 以压缩方式存储

  • 只提供Java SE 和JDK N - 出口的平台中和ral

  • Stored in a compressed fashion
  • Only provide Java SE N and JDK N-exported APIs that are platform neutral

的主要优点 - 发布N 方法:



  • 没有用户需要管理存储旧API信息的工件

  • 应该删除需要使用像Maven插件动物嗅探器

  • 可能会使用较旧版本的 javac 中的较新的编译成语

  • No user need to manage artifacts storing old API information
  • Should remove need to use tools like the Maven plugin Animal Sniffer
  • May use newer compilation idioms than the javac in older releases


  • 修复错误

  • 改进速度






Java 9和Maven上的更新

由于版本 3.6.0 maven-compiler-plugin 通过其 release 选项:


Update on Java 9 and Maven
Since version 3.6.0, the maven-compiler-plugin provides support for Java 9 via its release option:




An example:

<plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.6.0</version>
    <configuration>
        <release>8</release>
    </configuration>
</plugin>

这篇关于Eclipse / Idea忽略了Maven Java版本配置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆