我有一个我想要创建的java项目,它将构建在一些供应商API之上。 API连接到运行所述供应商软件的服务器并执行各种操作。
我有两个不同版本的服务器支持2个不同的API版本。对API的任何更改仅在内部实现中。 I.E.旧版本中可用的类,接口,方法等存在于较新版本中。因此,我编写的代码应该使用API版本编译和运行。在使用API进行连接时,API中提供的版本号会阻止您在该特定服务器上使用不同的版本API。
有没有办法在运行时动态切换JAR文件? (类似于c / c ++ DLL ??)
如果无法在运行时切换API版本,那么处理问题的最佳方式是什么。构建代码2x(每个api版本一个)?
我希望我错过了一些东西但是方法2似乎并不理想。这是一个更具体的例子:
package org.myhypotheticalwrapper.analyzer;
import org.myhypothetical.worker;
import org.myhypothetical.comparator;
public class Analyzer {
Worker w1 = new Worker();
Worker w2 = new Worker();
Comparator c = new Comparator(w1.connectAndDoStuff(),w2.connectAndDoStuff());
c.generateReport();
}
这是我的困境。我希望使用旧API构建w1,并使用新API构建w2,以便它们可以连接到相应的服务器。除了他们坐在上面的API之外,它们是相同的(相同的代码)。我是否必须为W1和W2创建两个唯一命名的类类型,即使它们的代码相同,只是为了适应不同的API版本?如果我有许多我希望与之交互的API版本,那么这似乎很快就会变得笨拙。
非常感谢任何建议和意见。
-new guy
答案 0 :(得分:4)
最简单的可能是在不在默认类路径中的类中加载类加载器。
来自http://www.exampledepot.com/egs/java.lang/LoadClass.html
// Convert File to a URL
URL url = file.toURL(); // file:/c:/myclasses/
URL[] urls = new URL[]{url};
// Create a new class loader with the directory
ClassLoader cl = new URLClassLoader(urls);
// Load in the class; MyClass.class should be located in
// the directory file:/c:/myclasses/com/mycompany
Class cls = cl.loadClass("com.mycompany.MyClass");
答案 1 :(得分:2)
一旦加载了类文件,就无法真正更改它,因此在运行时无法替换类。请注意像JavaRebel之类的项目通过javaagent巧妙地使用了一些工具来解决这个问题 - 但即使你能用它做什么也是有限的。
从它的声音中你只需要在你的环境中同时有两个并行实现,而不需要在运行时重新加载类。这很容易实现。假设您的运行时包含以下文件:
假设您的工作者界面代码如下所示:
package com.example.api;
public interface Worker {
public Object connectAndDoStuff();
}
你的实现(在v1和v2中)看起来像这样:
package com.example.impl;
import com.example.api.Worker;
public class WorkerImpl implements Worker {
public Object connectAndDoStuff() {
// do stuff - this can be different in v1 and v2
}
}
然后您可以像这样编写分析器:
package com.example.analyzer;
import com.example.api.Worker;
public class Analyzer {
// should narrow down exceptions as needed
public void analyze() throws Exception {
// change these paths as need be
File apiImplV1Jar = new File("api-impl-v1.jar");
File apiImplV2Jar = new File("api-impl-v2.jar");
ClassLoader apiImplV1Loader =
new URLClassLoader(new URL[] { apiImplV1Jar.toURL() });
ClassLoader apiImplV2Loader =
new URLClassLoader(new URL[] { apiImplV2Jar.toURL() });
Worker workerV1 =
(Worker) apiImplV1Loader.loadClass("com.example.impl.WorkerImpl")
.newInstance();
Worker workerV2 =
(Worker) apiImplV2Loader.loadClass("com.example.impl.WorkerImpl").
.newInstance();
Comparator c = new Comparator(workerV1.connectAndDoStuff(),
workerV2.connectAndDoStuff());
c.generateReport();
}
}
要运行分析器,您可以在类路径中包含analyzer.jar和api.jar,但忽略 api-impl-v1.jar和api-impl-v2.jar。 / p>
答案 2 :(得分:0)
您需要确保这些类位于不同的包中。您不能导入具有相同包列表的两个jar文件,并且期望一个文件被识别为另一个。如果它们位于不同的包中,您可以使用:
com.foo.Worker w1;
com.bar.Worker w2;
答案 3 :(得分:0)
您的工作人员需要拥有实现接口的委托。你必须写两个代表,一个代表旧api,一个代表新代表。选择要在运行时实例化的委托。类似的东西:
public class Worker {
private WorkerDelegate delegate;
public void foo() { delegate.foo(); }
public Object bar(){ return delegate.bar(); }
public Enum API{v1,v2};
public Worker(API api) {
try { switch(api){
case v1: delegate = Class.forName("my.v1.impl").newInstance(); break
case v2: delegate = Class.forName("my.v2.impl").newInstance(); break
}
}catch(...){throw new Error(e);}
}
}
以后可以轻松添加更多实现。