使用不同的Java版本编译软件包类

3
我有一个分布式应用程序,其中一部分是遗留的,因此在Java 7下运行。另一部分在Java 8下运行(当然也编译)。从应用程序的第一部分调用第二个VM的服务会导致以下错误:

Exception in thread "main" java.lang.UnsupportedClassVersionError: Unsupported major.minor version 52.0

我决定使用Maven编译器插件将所需服务编译为Java 7源/目标。因此,假设我们有一个包含两个类的包:
com/example/A.java
com/example/B.java

如何配置Maven编译插件,或者是否有其他方法?使用Java 7编译A.java,并使用Java 8编译其他(B.java)。

我理解你的意思是:您想在不同的JVM上运行两个Java应用程序,一个在Java 7上,另一个在Java 8上?为什么这些类是同一项目/ jar的一部分? - J Fabian Meier
是的,一个应用程序在Java7虚拟机下运行,另一个在Java8虚拟机下运行。Java7虚拟机应用程序依赖于一个包,该包也用于Java8虚拟机应用程序,并在Java8下编译。因此,这个依赖关系(简化)有两个服务,一个是为Java7虚拟机应用程序提供的,另一个是为Java8虚拟机应用程序提供的。 - andymur
将不同的项目分成两个,一个用于Java 8,另一个用于Java 7,并分开处理事务... - khmarbaise
我很感激你的回复! - GhostCat
2个回答

7
你正在错误的兔子洞里探寻。不要将产品/部署单元/交付视为边界。包只是有助于组织源码的东西。没有别的作用。
考虑针对不同包的不同版本纯属浪费时间,是在错误的问题上花费时间。
相反:理解你的交付方式,然后从这里决定应该以什么版本编译哪个产品。
此外:使用服务可以远远超过调用其他类。如果你真的有独立运行但需要彼此使用的组件,那么就定义这些服务,并使用定义好的协议互相连接。
例如:JVM A提供REST API,而JVM B则使用它来调用由JVM A提供/托管的服务。这样一来,你就不再被强制考虑Java版本 - 相反地,你可以专注于清晰地定义接口及其使用方法。或者使用某种消息总线。有很多选择。就像我说的:把你的时间和精力花在这上面,而不是控制包边界上的java版本。
我的建议听起来可能很夸张:但是,你拥有运行不同版本JVM的事实已表明你正在面临一个更复杂的"现实世界"方案。在复杂的现实世界情况下,你最好采用真正的解决方案(例如服务定义),而不是绕道而行。

3
免责声明:@GhostCat的答案提供了适当的指导,真正的问题在于交付和打包过程,而不是编译。虽然强烈不建议这样做,但有可能实现所需功能。为了挑战自己,我将尝试具体回答这个问题:

如何配置Maven编译器插件,使A.java使用Java7编译,而其他文件(B.java)使用Java8编译。

您可以定义多个Maven编译器插件执行,每个执行包括或排除所需的类集,并使用不同的源/目标版本。例如,使用配置文件:

<profiles>
    <profile>
        <id>compile-java7</id>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.7.0</version>
                    <executions>
                        <execution>
                            <id>default-compile</id>
                            <goals>
                                <goal>compile</goal>
                            </goals>
                            <configuration>
                                <source>1.7</source>
                                <target>1.7</target>
                                <includes>
                                    <include>**/*Java7.java</include>
                                </includes>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>
    <profile>
        <id>compile-java8</id>
        <build>
           <!-- define similar execution for java 8 -->
        </build>
    </profile>
</profiles>

使用配置文件,您需要运行两次构建(使用mvn ... -Pcompile-java7等),每个Java版本都要运行一次。您还可以进行类似的设置,允许仅运行一次构建,例如定义执行输出Java 7和Java 8的输出文件夹等。其想法是使用适当的包含或排除定义不同的执行程序,使用不同的源和目标环境。
例如,将每个类版本输出到单独的文件夹中:
  • 跳过默认编译器执行
  • 使用-d标志为Java 7和Java 8定义执行
  • 由于javac无法创建文件夹,因此您必须确保它们被生成(例如使用AntRun插件)
这样您就会得到类似以下的结果:
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.7.0</version>
            <executions>
                <execution>
                    <id>default-compile</id>
                    <phase>compile</phase>
                    <goals>
                        <goal>compile</goal>
                    </goals>
                    <configuration>
                        <skipMain>true</skipMain>
                    </configuration>
                </execution>
                <execution>
                    <id>compile-java7</id>
                    <phase>compile</phase>
                    <goals>
                        <goal>compile</goal>
                    </goals>
                    <configuration>
                        <source>1.7</source>
                        <target>1.7</target>
                        <compilerArgs>
                            <arg>-d</arg>
                            <arg>${build.outputDirectory}/java7</arg>
                        </compilerArgs>
                        <includes>
                            <include>**/*Java7.java</include>
                        </includes>
                    </configuration>
                </execution>
                <execution>
                    <id>compile-java8</id>
                    <phase>compile</phase>
                    <goals>
                        <goal>compile</goal>
                    </goals>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <compilerArgs>
                            <arg>-d</arg>
                            <arg>${build.outputDirectory}/java8</arg>
                        </compilerArgs>
                        <includes>
                            <include>**/*Java8.java</include>
                        </includes>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-antrun-plugin</artifactId>
            <version>1.8</version>
            <executions>
                <execution>
                    <phase>validate</phase>
                    <configuration>
                        <tasks>
                            <echo message="Creating test output directory"/>
                            <mkdir dir="./target/classes/java7"/>
                            <mkdir dir="./target/classes/java8"/>
                        </tasks>
                    </configuration>
                    <goals>
                        <goal>run</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

请注意,虽然可能可以这样做,但这仍然是一个不好的主意:
  • 问题最好通过回顾项目的交付方法和架构来解决 如GhostCat所指出的那样
  • 你过度复杂化了构建过程 (那就是纯插件配置的折磨!)
  • 你覆盖了大部分Maven的默认行为

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