我确定这是一个愚蠢的问题,但是..我们有相同的Java源文件,我们想要使用不同版本的Java API(jar文件),具体取决于我们构建应用程序的客户端。
较新版本的API具有我们在Java源代码中引用的方法setAAA()和setBBB():
if (...) {
api.setAAA(a);
api.setBBB(b);
}
如果使用旧API编译,旧代码API没有这些setter,则此代码将失败。如果我们使用新的API,有没有办法将此代码条件化为仅编译setter行?
感谢。
答案 0 :(得分:5)
最安全的方法是回退到您需要支持的最低版本。这假设所有版本都向后兼容,但情况并非如此。
如果该解决方案不合适或不合适,那么我会回到dependency injection。 Spring framework是目前最流行和最常见的DI框架,但并不是唯一的。 Guice是另一个。如果不希望为此添加完整的框架,您甚至可以自己动手。
但是我设想一个Java应用程序 - 特别是Web / J2EE应用程序 - 我没有使用Spring就会遇到问题。它太有用了。
假设有4个版本的相关jar。 API在此期间已更改两次,因此您有3种不同的API版本。您需要将该jar的使用抽象为您自己的API,这些API在所有这些版本中都是一致的,然后创建它的三个实现:每个不同的API版本一个。
在Spring中,您创建了一个应用程序上下文,它定义了所有bean以及它们如何注入其他bean。没有理由不能选择或构建应用程序上下文作为构建过程的一部分。通常使用属性,但您也可以通过这种方式包含部分应用程序上下文。
这里的关键点是,尽管API不同,但就代码而言,您需要抽象出这些差异。如果你不这样做,你只是在寻找麻烦而且它会变得更加混乱。
答案 1 :(得分:2)
Java真的不适合这种条件编译(不像C ++),老实说听起来像是一个以“类路径地狱”结束的秘诀。
虽然您可以手动开始处理返回API版本的函数,但是您有一个适合特定版本的类文件,但没有迹象表明它可能不兼容。
我之前遇到过这种情况(例如,使用不同版本的Eclipse)并且它并不漂亮。我最终做的是拥有一个具有两个不同实现的接口,每个API一个,将每个API放在一个单独的项目中(在我的情况下是一个插件),然后尝试用工厂或注入加载它们。尽可能隔离它们。
答案 2 :(得分:1)
您还可以保留版本控制系统的单独分支,其中包含客户特定的(即特定于版本的)代码
答案 3 :(得分:1)
我过去所做的是:尽可能干净地编写与库的版本相关方面交互的最少量代码。为每个版本的库提供此代码的版本。让它们都实现相同的界面。应用程序的大部分应该尝试(使用Class.forName
并且可能使用 little 反射进行构建)动态加载适合最新库的版本。如果失败,请回退到旧库的静态链接版本。
通过适当使用sourcepath和classpath,您可以安排防止您的核心代码使用新库。
答案 4 :(得分:1)
您可以编译到最小公分母,然后使用反射来调用仅在以后的API上可用的方法。例如,假设在类com.foo.Bar上,方法“getFoogle()”在API的更高版本中被“getFiggle()”方法取代。让我们假设方法(在任一变体中)采用int和double并返回int。您按如下方式进行包装调用:
public int getFoogleFiggle(Bar bar, int n, double d) {
try {
Class clz = Class.forName("com.foo.Bar");
Method m = clz.getMethod("getFiggle", new Class[] {Integer.class, Double.class});
return (Integer) m.invoke(bar, new Object[] {n, d});
} catch (NoSuchMethodException nsme) {
return getFoogle(n, d);
} catch (various other spurious exceptions) {
... deal with in intesresting ways ...
}
}
请注意,在 compile 时,编译器不关心类coo.foo.Bar和/或方法getFiggle是否存在。
答案 5 :(得分:0)
您可以使用java introspection。看看包装:
<强> java.lang.reflect中强>
它有一个名为Method的类。您可以使用以下方法获取类的所有公共方法:
Method[] methodList = obj.getClass().getMethods();
由于它是一个API,因此setter将是公共的。然后,您可以运行数组methodList并检查那些与setter同名的方法。如果找到它们,请使用它们。否则,您知道这是早期版本。
此外,大多数开发良好的API都有一个函数,它返回当前版本的JAR文件的值。
例如:
String currentVersion = api.SomeClass.version() ;
尝试检查您使用的API中是否存在类似的功能。这会更容易。
答案 6 :(得分:0)
我有同样的需求,因为我们的代码需要在Java 1.2的所有Java版本上运行,但有些代码需要利用更新的API(如果可用的话)。
在使用反射获取方法对象并动态调用它们的各种排列之后,我总体上已经确定了最好的包装样式方法(尽管在某些情况下,只是将反射的方法存储为静态并调用它更好 - 它取决于。)
以下是一个示例“系统实用程序”类,它公开了某些较新的API。此示例使用Singleton,但如果底层API需要,则可以轻松实例化多个对象。
有两个类:
如果运行时JVM是Java 5或更高版本,则使用后者。否则,在SysUtil中的默认实现中使用兼容兼容的回退方法,该实现仅使用Java 4或更早的API。每个类都使用特定版本的编译器进行编译,因此Java 4类中不会意外使用Java 5+ API:
import java.io.*;
import java.util.*;
/**
* Masks direct use of select system methods to allow transparent use of facilities only
* available in Java 5+ JVM.
*
* Threading Design : [ ] Single Threaded [x] Threadsafe [ ] Immutable [ ] Isolated
*/
public class SysUtil
extends Object
{
/** Package protected to allow subclass SysUtil_J5 to invoke it. */
SysUtil() {
super();
}
// *****************************************************************************
// INSTANCE METHODS - SUBCLASS OVERRIDE REQUIRED
// *****************************************************************************
/** Package protected to allow subclass SysUtil_J5 to override it. */
int availableProcessors() {
return 1;
}
/** Package protected to allow subclass SysUtil_J5 to override it. */
long milliTime() {
return System.currentTimeMillis();
}
/** Package protected to allow subclass SysUtil_J5 to override it. */
long nanoTime() {
return (System.currentTimeMillis()*1000000L);
}
// *****************************************************************************
// STATIC PROPERTIES
// *****************************************************************************
static private final SysUtil INSTANCE;
static {
SysUtil instance=null;
try { instance=(SysUtil)Class.forName("SysUtil_J5").newInstance(); } // can't use new SysUtil_J5() - compiler reports "class file has wrong version 49.0, should be 47.0"
catch(Throwable thr) { instance=new SysUtil(); }
INSTANCE=instance;
}
// *****************************************************************************
// STATIC METHODS
// *****************************************************************************
/**
* Returns the number of processors available to the Java virtual machine.
* <p>
* This value may change during a particular invocation of the virtual machine. Applications that are sensitive to the
* number of available processors should therefore occasionally poll this property and adjust their resource usage
* appropriately.
*/
static public int getAvailableProcessors() {
return INSTANCE.availableProcessors();
}
/**
* Returns the current time in milliseconds.
* <p>
* Note that while the unit of time of the return value is a millisecond, the granularity of the value depends on the
* underlying operating system and may be larger. For example, many operating systems measure time in units of tens of
* milliseconds.
* <p>
* See the description of the class Date for a discussion of slight discrepancies that may arise between "computer time"
* and coordinated universal time (UTC).
* <p>
* @return The difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.
*/
static public long getMilliTime() {
return INSTANCE.milliTime();
}
/**
* Returns the current value of the most precise available system timer, in nanoseconds.
* <p>
* This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock
* time. The value returned represents nanoseconds since some fixed but arbitrary time (perhaps in the future, so values
* may be negative). This method provides nanosecond precision, but not necessarily nanosecond accuracy. No guarantees
* are made about how frequently values change. Differences in successive calls that span greater than approximately 292
* years (263 nanoseconds) will not accurately compute elapsed time due to numerical overflow.
* <p>
* For example, to measure how long some code takes to execute:
* <p><pre>
* long startTime = SysUtil.getNanoTime();
* // ... the code being measured ...
* long estimatedTime = SysUtil.getNanoTime() - startTime;
* </pre>
* <p>
* @return The current value of the system timer, in nanoseconds.
*/
static public long getNanoTime() {
return INSTANCE.nanoTime();
}
} // END PUBLIC CLASS
import java.util.*;
class SysUtil_J5
extends SysUtil
{
private final Runtime runtime;
SysUtil_J5() {
super();
runtime=Runtime.getRuntime();
}
// *****************************************************************************
// INSTANCE METHODS
// *****************************************************************************
int availableProcessors() {
return runtime.availableProcessors();
}
long milliTime() {
return System.currentTimeMillis();
}
long nanoTime() {
return System.nanoTime();
}
} // END PUBLIC CLASS
答案 7 :(得分:-1)
您可以尝试
基于反射的调用或代码生成或旧的预处理技术或
策略模式以封装不同的内容。
class ThirdPartyApi {
void foo(){} // available in all versions
void bar(){} // available only in new version
}
ThirdPartyApiV1 extends ThirdPartyApi {
void foo() {
thirdpartyV1Object.foo();
}
}
ThirdPartyApiV2 extends ThirdPartyApi {
void foo() {
thirdpartyV2Object.foo();
}
void bar() {
thirdpartyV2Object.bar();
}
}
使用DependencyInjection注入正确版本的ThridPartyApi实现。 否则,使用ThirdPartyApiFactory根据配置或系统属性值创建适当的实例。