使用不同的Java版本编译Maven模块

34

我的maven项目有几个模块:服务器、Web等。

我想在Java 6上构建除了服务器模块之外的所有模块。对于服务器模块,我想使用Java 7进行编译。

下面是我的pom.xml文件,但我认为如果将其修改为1.7,则所有模块都将使用Java 7进行编译。另外,maven是否使用JAVA_HOME环境变量来确定要使用哪个Java版本?

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
                        <version>2.3.2</version>
        <configuration>
            <source>1.6</source>
            <target>1.6</target>
            <memmax>2048m</memmax>
        </configuration>
    </plugin>

编辑: 另外,下面的输出结果

maven --version

是否表示maven正在使用1.7来编译我的Java代码?

vagrant@dev:~/bin/apache-tomcat-7.0.29/bin$ mvn --version
Apache Maven 3.0.4 (r1232337; 2012-01-17 08:44:56+0000)
Maven home: /home/vagrant/bin/apache-maven-3.0.4
Java version: 1.7.0_07, vendor: Oracle Corporation
Java home: /home/vagrant/bin/jdk1.7.0_07/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "3.2.0-23-generic", arch: "amd64", family: "unix"

感谢,Kevin

3个回答

38

有很多方法可以在编译源代码时使用不同版本的JDK,而不是使用运行Maven所需的版本。例如,您可以使用以下方法:

<project>
  [...]
  <build>
    [...]
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.5.1</version>
        <configuration>
          <executable><!-- path-to-javac --></executable>
        </configuration>
      </plugin>
    </plugins>
    [...]
  </build>
  [...] 
</project>

这种方法的问题在于你现在已经将JDK路径硬编码到了POM中。一切都能在你自己的机器上正常运行,但是当你不得不重建你的机器因为硬盘损坏或者你想在另外一台机器上构建时,你会陷入困境,因为路径大概率不匹配。
正确的最佳实践是通过工具链来处理。这将使你创建一个~/.m2/toolchains.xml文件,描述系统中每个不同工具链的位置。然后可以通过Maven Toolchains插件应用JDK的版本,例如:
<plugins>
 ...
  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-toolchains-plugin</artifactId>
    <version>1.0</version>
    <executions> 
      <execution>
        <phase>validate</phase>
        <goals>
          <goal>toolchain</goal>
        </goals>
      </execution>
    </executions>
    <configuration>
      <toolchains>
        <jdk>
          <version>1.6</version>
        </jdk>
      </toolchains>
    </configuration>
  </plugin>
  ...
</plugins>

下一步是,你不需要经常使用它。例如,通过使用“source”和“target”值,您可以为您正在针对的JRE生成正确的字节码...然后唯一的问题是使用了JRE 1.7中新方法时会遇到的问题...这就是Mojo's Animal Sniffer Plugin发挥作用的地方。Animal Sniffer可用于确保仅使用您正在针对的JRE的方法。社区普遍认为,在Maven编译器插件配置中使用“source”和“target”配置选项以及使用Mojo's Animal Sniffer几乎消除了编译器端工具链的需要......在Surefire端仍需要工具链......我有一些边缘情况需要更新编译器插件和工具链插件来处理,但实际上您不会遇到那些边缘情况;-)
只是为了确保完全回答您的原始问题(因为上面回答了您想要问的问题-而不是您实际问的问题)
目前您正在使用JDK 1.7进行编译,然而根据您使用的Maven编译插件版本,您可能会使用以下任一版本进行编译:<source>1.4</source><target>1.4</target><source>1.5</source><target>1.5</target>,除非您已更改了pom.xml中的Maven编译插件配置。这将决定您可以使用哪些语言特性,但不决定可以使用哪些类...因此,您将生成适用于JRE 1.7的代码,并且只要您没有使用自1.4/1.5以来引入的任何新类/方法(例如String.isEmpty()),它也应该适用于JRE 1.4/1.5...确保其在旧的JVM上工作的唯一方法是:在旧的JVM上运行它或使用Animal Sniffer。

1
就像为您的pom添加maven-compiler-plugin和添加<source>和<target>配置选项一样,为了设置编译器插件检查的一些属性,可以添加一个小快捷方式 '<properties> <java.version>1.7</java.version><maven.compiler.source>${java.version}</maven.compiler.source> <maven.compiler.target>${java.version}</maven.compiler.target>' - dan carter
1
编辑此顶级答案以澄清正确的交叉编译新最佳实践是否有意义,即使用javac --release标志(请参见https://dev59.com/RVgQ5IYBdhLWcg3wORZJ#43103038/)?就配置而言,它要容易得多,速度更快(无需为每个模块分叉进程),适用于Java 7+。如果需要在目标JVM上运行测试或编译古老的Java(6或更早版本),则仍然可以使用工具链。 - Dmitry Timofeev

9

Maven工具链

如果你需要使用多个Java版本,你需要使用Maven工具链,这要求你在~/.m2 Maven文件夹中创建一个toolchains.xml文件,其中包含你机器上安装的所有Java版本:

<toolchains>
  <toolchain>
    <type>jdk</type>
    <provides>
      <id>Java13</id>
      <version>13</version>
    </provides>
    <configuration>
      <jdkHome>${env.JAVA_HOME_13}</jdkHome>
    </configuration>
  </toolchain>
  <toolchain>
    <type>jdk</type>
    <provides>
      <id>Java9</id>
      <version>9</version>
    </provides>
    <configuration>
      <jdkHome>${env.JAVA_HOME_9}</jdkHome>
    </configuration>
  </toolchain>
  <toolchain>
    <type>jdk</type>
    <provides>
      <id>Java8</id>
      <version>8</version>
    </provides>
    <configuration>
      <jdkHome>${env.JAVA_HOME_8}</jdkHome>
    </configuration>
  </toolchain>
  <toolchain>
    <type>jdk</type>
    <provides>
      <id>Java7</id>
      <version>7</version>
    </provides>
    <configuration>
      <jdkHome>${env.JAVA_HOME_7}</jdkHome>
    </configuration>
  </toolchain>
  <toolchain>
    <type>jdk</type>
    <provides>
      <id>Java6</id>
      <version>6</version>
    </provides>
    <configuration>
      <jdkHome>${env.JAVA_HOME_6}</jdkHome>
    </configuration>
  </toolchain>
</toolchains>

JAVA_HOME_13JAVA_HOME_9JAVA_HOME_8JAVA_HOME_7JAVA_HOME_6 这几个环境变量被配置为引用相关 Java 版本安装路径。

FlexyPool 的父 pom.xml 配置文件

FlexyPool 项目的父 pom.xml Maven 配置文件定义了全局 Java 版本设置。

<properties>
    <jdk.version>8</jdk.version>
    ...
</properties>

现在,我们需要指示编译器和测试插件使用配置的Java版本。
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-toolchains-plugin</artifactId>
            <version>1.1</version>
            <executions>
                <execution>
                    <goals>
                        <goal>toolchain</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <toolchains>
                    <jdk>
                        <version>${jdk.version}</version>
                    </jdk>
                </toolchains>
            </configuration>
        </plugin>
        
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>${maven-compiler-plugin.version}</version>
            <configuration>
                <source>${jdk.version}</source>
                <target>${jdk.version}</target>
                <showDeprecation>true</showDeprecation>
                <showWarnings>true</showWarnings>
            </configuration>
        </plugin>
        
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>${maven-surefire-plugin.version}</version>
        </plugin>
    </plugins>
</build>

使用不同的Java版本的FlexyPool子Maven模块pom.xml

flexy-pool-core-java9子Maven模块需要使用不同的Java版本,只需要覆盖默认的jdk.version Maven属性:

<properties>
    <jdk.version>9</jdk.version>
</properties>

现在我们可以使用各自的最小可行Java版本来构建每个模块。


环境变量即将不再支持,但仍然可以在$M2_HOME/conf/settings.xml中使用。(来源) - TheLonelyGhost
谢谢你的提示。 - Vlad Mihalcea

3

根据我在原帖中包含的maven --version输出,这是否意味着mvn正在使用1.7进行编译? - Kevin Meredith
根据您的输出,您将使用jdk 1.7编译应用程序,但您的代码将编译为jdk 1.6(或更高版本),这符合您的需求。 - Farid
请解释一下“mvn --version”输出与我的pom.xml中的<source>和<target>值的重要性。如果我想要专门使用1.7进行编译,这是否意味着我需要将<source>和<target>更改为1.7? - Kevin Meredith
mvn --version 可以让您查看当前正在使用的环境。关于源和目标参数,它们将指定 javac 工具如何设置相同的参数:请参阅以下链接中这些参数的描述:http://docs.oracle.com/javase/6/docs/technotes/tools/windows/javac.html - Farid
因此,在您的上下文中,您正在告诉JDK 1.7首先确保源代码仅包含1.6 VM及以下版本可用的功能,然后告诉它生成与JVM 1.6或更高版本兼容的二进制文件。 - Farid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接