如何为不同的Java版本(6、7、8)支持不同版本的主(和测试)源集
我有一个Java库项目。我想实现、测试并可能发布该项目的几个版本,这些版本打算与不同的Java版本一起使用:6、7、8 最简单的方法就是复制粘贴项目并支持多个源代码树,但我希望避免这种情况,因为这很繁琐且容易出错 另一种可能的方法是考虑“基本”项目和几个Java版本特定的项目,具体取决于。版本差异非常小,但我不知道如何在类层次结构中反映这个技术开发问题 所以我在找如何为不同的Java版本(6、7、8)支持不同版本的主(和测试)源集,java,maven,java-8,conditional-compilation,Java,Maven,Java 8,Conditional Compilation,我有一个Java库项目。我想实现、测试并可能发布该项目的几个版本,这些版本打算与不同的Java版本一起使用:6、7、8 最简单的方法就是复制粘贴项目并支持多个源代码树,但我希望避免这种情况,因为这很繁琐且容易出错 另一种可能的方法是考虑“基本”项目和几个Java版本特定的项目,具体取决于。版本差异非常小,但我不知道如何在类层次结构中反映这个技术开发问题 所以我在找 一种预编译器 和/或标准Maven选项 和/或Maven插件 这有助于从单个源代码树支持多个特定于Java版本的库版本,并且对库
- 一种预编译器
- 和/或标准Maven选项
- 和/或Maven插件
这有助于从单个源代码树支持多个特定于Java版本的库版本,并且对库用户透明。代码中嵌入了一种方法:从log4j2源代码:
// JDK 1.1 doesn't support readResolve necessary for the assertion
if (!System.getProperty("java.version").startsWith("1.1.")) {
assertTrue(obj == Level.INFO);
}
您还可以根据java版本使用和设置变量来更改代码。尽管在调试困难的情况下,它将在尽可能少的地方执行此操作。或者至少在运行动态代码之前打印一个日志,以便知道哪个版本/实际行有问题。您可以从单个pom.xml文件为每个Java版本(6、7、8)生成一个jar 所有相关工作都在魔咒中进行 诀窍是执行mojo 3次,每次都将结果文件写入不同的文件。这将导致编译器运行多次,每次使用不同的版本并生成一个适当命名的文件
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<executions>
<execution>
<id>compile-1.6</id>
<goals>
<id>compile</id>
</goals>
<phase>compile</phase>
<configuration>
<source>1.6</source>
<target>1.6</target>
<executable>${env.JAVA_HOME_6}/bin/javac</executable>
<outputFileName>mylib-1.6.jar</outputFileName>
</configuration>
<!-- START EDIT -->
<dependencies>
<dependency>
<groupId>com.mycompany</groupId>
<artifactId>ABC</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
<!-- END EDIT -->
</execution>
<execution>
<id>compile-1.7</id>
<phase>compile</phase>
<goals>
<id>compile</id>
</goals>
<configuration>
<source>1.7</source>
<target>1.7</target>
<executable>${env.JAVA_HOME_7}/bin/javac</executable>
<outputFileName>mylib-1.7.jar</outputFileName>
</configuration>
<!-- START EDIT -->
<dependencies>
<dependency>
<groupId>com.mycompany</groupId>
<artifactId>XYZ</artifactId>
<version>2.0</version>
</dependency>
</dependencies>
<!-- END EDIT -->
</execution>
<!-- one more execution for 1.8, elided to save space -->
</executions>
</plugin>
org.apache.maven.plugins
maven编译器插件
3.1
编译-1.6
编译
编译
1.6
1.6
${env.JAVA\u HOME\u 6}/bin/javac
mylib-1.6.jar
com.mycompany
基础知识
1
编译-1.7
编译
编译
1.7
1.7
${env.JAVA\u HOME\u 7}/bin/javac
mylib-1.7.jar
com.mycompany
XYZ
2
希望有帮助
编辑
RE:每个运行针对不同源进行编译的附加要求
请参见上面对pom代码段的编辑
每次执行都可以定义自己的依赖项库列表
因此,JDK6构建依赖于ABC.jar,而JDK7依赖于XYZ.jar。为什么不收集每个java版本中应该不同的方法,将它们包装在一个“实用程序”项目中,制作您自己的api供主代码调用,并在发布时添加您想要的实用程序jar 比如: util-api.jar(主项目调用的方法) util-1.6.jar(无论哪种实现适用,即使在无需执行任何操作的情况下,如果需要“无操作”)
我已经多次成功地解决了与您现在遇到的问题类似的问题。通过为每个java版本创建单独的概要文件,您可以有条件地包括一些源目录。然后,您可以使用profile name运行maven,它将定义您希望使用哪个版本的源。在这种情况下,所有公共源都保留在
src/main/java
中,而依赖于java版本的文件则放在/src/main/java-X.X目录中
<profiles>
<profile>
<id>java-6</id>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<id>add-sources</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${basedir}/src/main/java-1.6</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>java-7</id>
(...)
<source>${basedir}/src/main/java-1.7</source>
</profile>
<profile>
<id>java-8</id>
(...)
<source>${basedir}/src/main/java-1.8</source>
</profile>
</profiles>
对于这样的事情,我会
- 将JVM版本相关文件移动到它们自己的包中
- 或者,使用命名约定来标识特定于JVM版本的文件
- 用于编译,而不是Maven。Ant使基于名称或位置排除源文件变得容易。您还可以使用Ant轻松创建多个目标
- 如果库对所有JVM版本使用相同的API,我将为JVM特定的类创建接口,然后根据JVM版本在运行时实例化适当的实现
在决定迁移到基于基础的构建之前,从事这项工作的人员已经与这个问题搏斗了一段时间
查看了解更多信息。您需要做的是定义一个基本API,然后将新版本的Java可用功能添加为额外方法(可以从原始API方法调用)。这允许API的基本完整性从旧实现到新实现保持不变
例如:
public void forEach(Consumer<T> c);
public void forEach(消费者c);
是您的原始API方法,其中Consumer是您的org.mylib.Consumer。在Java8中,您可以用它做两件事。可以保留旧实现并添加新方法作为该实现的方便方法,也可以添加新实现并添加调用新实现的defender方法。无论哪种方式,实现API的遗留代码都将保持有效
方便方法示例:
//无需更改旧的Java 6样式实现
公共场所(消费者c);
默认void forEach(java.util.function.Consumer c){
//您的消费者需要j.u.f.C.的构造函数
消费者myC=新消费者(c);
forEach(myC);
}
Defender方法示例:
//Consumer扩展了j.u.f.C,因此它更具体,将覆盖
默认的void forEach(使用者c){
//我们所需要调用的另一个方法就是显式转换
forEach((java.util.function.Consumer)c);
}
//这里是全新的Java8风格实现
公共void forEach(java.util.function.consumerc);
显然,对于Java7版本,您无法在接口上定义默认方法,因此您需要
<project>
[...]
<build>
[...]
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${my.java.version}</source>
<target>${my.java.version}</target>
</configuration>
</plugin>
</plugins>
[...]
</build>
[...]
</project>
public void forEach(Consumer<T> c);
// No need to change the old Java 6 style implementation
public void forEach(Consumer<T> c);
default void forEach(java.util.function.Consumer<T> c){
// Your Consumer needs a constructor for j.u.f.C
Consumer<T> myC = new Consumer<T>(c);
forEach(myC);
}
// Consumer extends j.u.f.C so this is more specific and will override
default void forEach(Consumer<T> c){
// All we need to call the other method is an explicit cast
forEach((java.util.function.Consumer<T>) c);
}
//All new Java 8 style implementation here
public void forEach(java.util.function.Consumer<T> c);
public interface MyConsumer<T> extends Consumer<T> {
default void accept(T t){ // Formerly the abstract functional method
getConsumer().accept(t);
}
public Consumer<T> getConsumer(); //Our new abstract functional method
}
public interface MyIterable<T> extends Iterable<T> {
default void each(MyConsumer<? super T> action){
//Iterable.super.forEach((Consumer<? super T>) action);
//
// Or whatever else we need to do for our special
// class processing
}
default void each(Consumer<? super T> action){
if (action instanceof MyConsumer){
each((MyConsumer<? super T>) action);
} else {
Iterable.super.forEach(action);
}
}
@Override
default void forEach(Consumer<? super T> action){
each(action);
}
}
api.each(()->System.out::println);
api.each(System.out::println);