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>
然而,我没有问题声明:
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屋!