2009-07-22 83 views
2

我有一个我想要创建的java项目,它将构建在某些供应商API之上。这些API连接到运行该供应商软件的服务器并执行各种操作。是否可以在运行时使用Java切换Class版本?

我有2个不同版本的服务器支持2个不同的API版本。对API的任何更改仅在内部实现。 I.E.旧版本中可用的类,接口,方法等存在于较新版本中。因此,我编写的代码应该编译并使用任一API版本运行。使用API​​进行连接时,向服务器提供的API中存在一个版本号,可防止您在该特定服务器上使用不同的版本API。

  1. 有没有办法在运行时切换JAR文件? (类似于c/C++ DLL ??)

  2. 如果在运行时切换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(); 
} 

这是我的两难处境。我希望w1能够使用旧的API构建,并且使用新的API构建w2,以便它们可以连接到适当的服务器。除了API以外,它们都是相同的(相同的代码)。为了适应不同的API版本,我是否必须为W1和W2创建两个唯一命名的类类型,即使它们的代码是相同的?看起来,如果我有很多我想与之交互的API版本,它可能会很快变得笨拙。

任何意见和建议非常感谢。

回答

2

你不能真正改变了一个类文件,一旦它被加载,所以真的没有办法在运行时更换类。请注意,诸如JavaRebel这样的项目通过javaagent巧妙地使用仪器来解决这个问题 - 但即使您可以使用该工具也是有限的。

从它的声音你只需要在你的环境中同时有两个并行的实现,并且不需要在运行时重新加载类。这可以很容易地完成。假定运行时包括以下文件:

  • analyzer.jar - 这包含从上方
  • api.jar文件分析器/测试代码 - 这是常见的面向前的API代码,例如接口
  • API-IMPL-v1.jar - 这是旧版本的实施
  • API-IMPL-v2.jar - 这是实现

的较新版本假设你的工作人员接口代码看起来是这样的:

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(); 
    } 
} 

要运行分析器,那么你将包括在classpath analyzer.jar和api.jar文件,但离开了的API-IMPL-v1.jar和api-impl-v2.jar。

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"); 
0

你需要确保类在不同的包。您无法导入两个具有相同软件包列表的jar文件,并希望一个文件被另一个文件识别。如果他们在不同的包,你可以使用:

com.foo.Worker w1; 
com.bar.Worker w2; 
+0

只有当您有两个jar文件由同一个类加载器引用时,才会出现这种情况。如果您使用相同的类加载器,那么将使用一组类,而另一组不会使用。 但是,可能会在相同的JVM中加载具有相同包和相同名称的两个类。他们只需要加载不同的类加载器。 – 2009-07-22 19:10:58

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);} 
} 

} 

更多的实现可以稍后轻松添加。

相关问题