在同一Java源代码中处理不同的API版本
我肯定这是个愚蠢的问题,但是。。我们有相同的Java源文件,我们希望使用不同版本的Java API(jar文件),具体取决于我们构建应用程序的客户机 较新版本的API具有我们在Java源代码中引用的方法setAAA()和setBB():在同一Java源代码中处理不同的API版本,java,build-process,configuration-management,Java,Build Process,Configuration Management,我肯定这是个愚蠢的问题,但是。。我们有相同的Java源文件,我们希望使用不同版本的Java API(jar文件),具体取决于我们构建应用程序的客户机 较新版本的API具有我们在Java源代码中引用的方法setAAA()和setBB(): if (...) { api.setAAA(a); api.setBBB(b); } 如果使用旧API编译的代码中包含这些设置器,而旧API中没有这些设置器,则此代码将失败。如果我们正在使用新的API,有没有办法将此代码条件化为只编译setter行
if (...) {
api.setAAA(a);
api.setBBB(b);
}
如果使用旧API编译的代码中包含这些设置器,而旧API中没有这些设置器,则此代码将失败。如果我们正在使用新的API,有没有办法将此代码条件化为只编译setter行
谢谢。您可以使用java内省。看看包装: java.lang.reflect 它有一个名为Method的类。您可以使用以下方法获取类的所有公共方法:
Method[] methodList = obj.getClass().getMethods();
因为它是一个API,所以setter是公共的。然后,您可以运行数组methodList并检查那些与setter同名的方法。如果你找到了,就使用它们。否则,您知道这是早期版本
此外,大多数开发良好的API都有一个函数,返回JAR文件当前版本的值
例如:
String currentVersion = api.SomeClass.version() ;
尝试检查您正在使用的API中是否有类似的函数。这会更容易。最安全的方法是退回到需要支持的最低版本。这假设所有版本都是向后兼容的,但事实并非如此 如果这个解决方案不合适或不可取,那么我就求助于。到目前为止,是最流行和最常见的DI框架,但决不是唯一的一个。这是另一个。如果不希望为此添加完整的框架,您甚至可以自己滚动 但是我在设想一个Java应用程序时遇到了一些问题——特别是一个Web/J2EE应用程序——我没有使用Spring。它实在太有用了 假设一个相关jar有4个版本。API在此期间更改了两次,因此您有3个不同的API版本。您需要将jar的使用抽象为您自己的API,该API在所有这些版本中都是一致的,然后创建三个实现:一个用于不同的API版本 在Spring中,您创建了一个应用程序上下文,它定义了您的所有bean以及如何将它们注入到其他bean中。没有理由不能选择或构建应用程序上下文作为构建过程的一部分。属性通常用于此目的,但也可以通过这种方式包含应用程序上下文的一部分
这里的关键点是,即使API不同,您也需要将代码中的差异抽象出来。如果你不这样做,你只是在自找麻烦,它会变得更混乱。你也可以保留版本控制系统的不同分支,这些分支保存特定于客户(即特定于版本)的代码Java真的不适合这种条件编译(与C++不同),而且它听起来真的像是一种结束于“类路径地狱” 虽然您可以手动开始处理返回API版本的函数,但是您有一个适合特定版本的类文件,但没有任何迹象表明它可能不兼容
我以前也遇到过这种情况(例如,使用不同版本的Eclipse),但并不完美。我最终做的是使用两个不同实现的接口,每个API一个,将每个实现放在一个单独的项目中(在我的例子中是一个插件),然后尝试使用工厂或注入加载它们。尽可能地隔离它们。我过去所做的是:尽可能干净地编写与库的版本相关方面交互的最少代码量。为库的每个版本提供此代码的版本。让它们都实现相同的交互面。应用程序的大部分应该尝试(使用
Class.forName
和可能的少量构造反射)动态加载适用于最新库的版本。如果失败,请退回到旧库的静态链接版本
通过适当地使用sourcepath和classpath,可以防止核心代码使用新库。您可以尝试
- 基于反射的调用或代码生成或旧的预处理技术或
- 策略模式来封装变化的内容
否则,请使用第三方API工厂根据配置或系统属性值创建适当的实例。我也有同样的需求,因为我们有需要在Java 1.2的所有Java版本上运行的代码,但有些代码需要利用较新的API(如果有) 在使用反射来获取方法对象并动态调用它们的各种排列之后,我通常选择了包装器样式的方法作为最佳方法(尽管在某些情况下,仅将反射的方法存储为静态方法并调用它会更好—这取决于具体情况) 下面是一个示例“System Utility”类,它公开了某些较新的API。此示例使用单例,但如果底层API需要,可以轻松实例化多个对象 有两类:
- SysUtil
- SysUtil_J5
您可以编译到最低公分母,然后使用反射来调用仅在以后的API上可用的方法
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();
}
}
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
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 ...
}
}