POM包含(如https://stackoverflow.com/a/22398998/766786中所述):
<profile>
<id>compileWithJava5</id>
<!--
NOTE
Make sure to set the environment variable JAVA5_HOME
to your JDK 1.5 HOME when using this profile.
-->
<properties>
<java.5.home>${env.JAVA5_HOME}</java.5.home>
<java.5.libs>${java.5.home}/jre/lib</java.5.libs>
<java.5.bootclasspath>${java.5.libs}/rt.jar${path.separator}${java.5.libs}/jce.jar</java.5.bootclasspath>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
<compilerArguments>
<bootclasspath>${java.5.bootclasspath}</bootclasspath>
</compilerArguments>
</configuration>
</plugin>
</plugins>
</build>
</profile>设置了$JAVA5_HOME:
• echo $JAVA5_HOME
/usr/lib/jvm/jdk1.5.0_22就我所理解的Java+Maven的魔力而言,这应该是maven-compiler-plugin的一个有效咒语,它指示JDK1.8假装为JDK1.5并使用Java5引导类路径。
根据为什么javac在@重写注释上失败,JDK1.5将不允许在接口的实现方法上使用@Override,只允许在超类中存在的重写方法上使用@Override。
在此承诺中,@Override注释用于接口的实现方法,因此这是无效的Java5代码:
private static class DummyEvent implements PdfPTableEvent {
@Override
public void tableLayout(PdfPTable table, float[][] widths, float[] heights, int headerRows, int rowStart, PdfContentByte[] canvases) {
}
}当我跑的时候
mvn clean compile test-compile -P compileWithJava5包含@Override注释的类没有编译错误。我在这里错过了什么?
(已经尝试过:动物嗅探器Maven插件,但是插件不查看编译标志,只在字节码上。)
编辑:这是我目前在POM中拥有的内容。
<profile>
<id>compileWithLegacyJDK</id>
<!--
NOTE
Make sure to set the environment variable JAVA5_HOME
to your JDK 1.5 HOME when using this profile.
-->
<properties>
<java.version>1.5</java.version>
<java.home>${env.JAVA5_HOME}</java.home>
<java.libs>${java.home}/jre/lib</java.libs>
<java.bootclasspath>${java.libs}/rt.jar${path.separator}${java.libs}/jce.jar</java.bootclasspath>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<compilerArguments>
<bootclasspath>${java.bootclasspath}</bootclasspath>
</compilerArguments>
<compilerVersion>${java.version}</compilerVersion>
<fork>true</fork>
<executable>${java.home}/bin/javac</executable>
</configuration>
</plugin>
</plugins>
</build>
</profile>同跑
export JAVA5_HOME=/var/lib/jenkins/tools/hudson.model.JDK/1.5
mvn compile test-compile -P compileWithLegacyJDK有关更多细节,请参见下面接受的答案。
发布于 2016-10-18 22:03:28
问题的核心: Maven仍然在用启动它的JDK编译代码。由于您使用的是JDK 8,所以它使用JDK 8进行编译,要使用另一个编译器进行编译,您需要使用工具链或指定正确的JDK路径。
设置
要测试这个答案,您可以使用以下POM进行一个简单的Maven项目
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
<compilerArguments>
<bootclasspath>/usr/lib/jvm/jdk1.5.0_22/jre/lib/rt.jar</bootclasspath>
</compilerArguments>
</configuration>
</plugin>
</plugins>
</build>
</project>在src/main/java/test下有一个要编译的类,即:
package test;
interface I {
void foo();
}
public class Main implements I {
public static void main(String[] args) {
new Main().foo();
}
@Override
public void foo() {
System.out.println("foo");
}
}这看起来像一个配置为使用JDK 5的标准Maven项目。注意,该类在实现接口的方法上使用@Override。这在Java 6之前是不允许的。
如果您试图在JDK 8下运行Maven来构建这个项目,那么它将编译,尽管设置了<source>1.5</source>。
它为什么要编译?
Maven编译器插件没有错。javac应该受到责备。设置-source标志并不告诉javac用这个特定的JDK版本编译您的项目。它指示javac只接受特定版本的源代码。来自文档
-source release:指定接受的源代码版本。
例如,如果您指定了-source 1.4,那么您要编译的源代码就不能包含泛型,因为这些泛型是后来引入到语言中的。该选项强制执行应用程序的源兼容性。使用Java 5泛型的Java应用程序与使用JDK 4编译器的Java 4程序不兼容。同样,使用Java 8 lambda表达式的应用程序与JDK 6编译器不兼容源代码。
在这种情况下,@Override是Java 5中已经存在的注释。然而,它的语义在Java6中发生了变化。因此,使用@Override的代码,无论它是否在实现接口的方法上,都是与Java5程序兼容的源代码。因此,在这样的类上使用-source 1.5运行JDK 8不会失败。
为什么要跑?
转到第二个参数:target。同样,这不是Maven编译器的问题,而是javac的问题。-source标志强制源代码与旧版本兼容,而-target则强制执行与旧版本的二进制兼容性。此标志告诉javac生成与较旧版本的JVM兼容的字节代码。它没有告诉javac检查编译后的代码是否真的可以与旧版本的JVM一起运行。为此,您需要设置一个bootclasspath,它将将您的代码与指定的JDK交叉编译。
显然,实现接口的方法上的@Override不能在Java5VM上运行,所以javac应该在这里运行。但是,nope:Override有源保留,这意味着在编译发生后注释将被完全丢弃。这也意味着当交叉编译发生时,注释不再存在;它在用JDK 8编译时被丢弃了。正如您所发现的,这也是为什么像动物嗅探插件这样的工具(它支持带有预定义JDK版本的自动bootclasspath )不会检测到这一点:注释丢失了。
总之,您可以将上面的示例应用程序打包为运行在JDK 8上的mvn clean package,并运行它,而不会碰到Java5JVM上的任何问题。它将打印"foo"。
我怎么能使它不编译呢?
有两种可能的解决办法。
第一个,直接的javac,通过编译器插件的executable属性:
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
<compilerArguments>
<bootclasspath>/usr/lib/jvm/jdk1.5.0_22/jre/lib/rt.jar</bootclasspath>
</compilerArguments>
<compilerVersion>1.5</compilerVersion>
<fork>true</fork>
<!-- better to have that in a property in the settings, or an environment variable -->
<executable>/usr/lib/jvm/jdk1.5.0_22/bin/javac</executable>
</configuration>
</plugin>这将设置编译器应该与compilerVersion参数一起使用的JDK的实际版本。这是一种简单的方法,但请注意,它只更改用于编译的JDK版本。Maven仍然将使用JDK 8安装来生成Javadoc或运行单元测试,或者任何需要JDK安装工具的步骤。
第二种是全局的方法,就是使用工具链。这些命令将指示Maven使用与启动mvn不同的JDK,然后每个Maven插件(或任何知道工具链的插件)都将使用此JDK来执行它们的操作。编辑POM文件以添加maven-toolchains-plugin的以下插件配置
<plugin>
<artifactId>maven-toolchains-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<goals>
<goal>toolchain</goal>
</goals>
</execution>
</executions>
<configuration>
<toolchains>
<jdk>
<version>1.5</version>
</jdk>
</toolchains>
</configuration>
</plugin>缺少的成分是告诉那些插件,该工具链的配置在哪里。这是在toolchains.xml文件中完成的,通常在~/.m2/toolchains.xml文件中完成。从Maven 3.3.1开始,您可以使用--global-toolchains参数定义该文件的位置,但最好将其保存在用户家中。内容如下:
<toolchains>
<toolchain>
<type>jdk</type>
<provides>
<version>1.5</version>
</provides>
<configuration>
<jdkHome>/usr/lib/jvm/jdk1.5.0_22</jdkHome>
</configuration>
</toolchain>
</toolchains>这声明了一个类型为jdk的工具链,提供了一个JDK 5和JDK主页的路径。Maven插件现在将使用这个JDK。实际上,它也是编译源代码时使用的JDK。
如果您试图再次编译上面添加的配置的示例项目.您最终会遇到错误:
方法不覆盖其超类中的方法。
发布于 2016-10-14 12:17:28
当您使用target时,将JDK 1.8设置为1.5并不能保证您的代码将在1.5上工作,这在maven-compiler-plugin的文档中已经解释过了。
仅仅设置
target选项并不能保证您的代码在具有指定版本的JRE上实际运行。陷阱是仅存在于以后的JRE中的API的意外使用,这会使您的代码在运行时出现链接错误而失败。为了避免这个问题,您可以配置编译器的引导类路径来匹配目标JRE,或者使用动物嗅探器Maven插件来验证您的代码没有使用意外的API。
https://stackoverflow.com/questions/40042878
复制相似问题