2009-07-22 2 views
2

У меня есть проект java, который я хочу создать, который будет построен поверх некоторых API-интерфейсов поставщиков. API-интерфейсы подключаются к серверам, на которых выполняется указанное программное обеспечение поставщика, и выполняют различные действия.Можно ли переключать версии класса во время выполнения с Java?

У меня есть 2 разных версии серверов, поддерживаемых двумя различными версиями API. Любые изменения API API выполняются только во внутренней реализации. И.Е. Классы, интерфейсы, методы и т. Д., Доступные мне в старой версии, существуют в более новой версии. Поэтому код, который я пишу, должен компилироваться и запускаться с любой версией 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 и w2 был создан с новым API, чтобы они могли подключаться к соответствующим серверам. Кроме API, они сидят сверху, они одинаковы (идентичный код). Должен ли я создавать два уникально названных типа класса для W1 и W2, даже если их код идентичен, просто для размещения разных версий API? Похоже, что это может стать очень громоздким, если бы у меня было много версий API, с которыми я хотел бы взаимодействовать.

Любые предложения и комментарии очень ценятся.

-new парень

ответ

2

Вы не можете изменить из файла класса, как только он был загружен, так что на самом деле нет способа заменить класс во время выполнения. Обратите внимание, что проекты, подобные JavaRebel, обойдусь этим с помощью умного использования инструментов с помощью javaagent, но даже то, что вы можете сделать с этим, ограничено.

Из-за звуков вам просто нужно иметь одновременно две параллельные реализации в вашей среде и не нужно перезагружать классы во время выполнения. Это можно сделать довольно легко. Предположим, ваша среда состоит из следующих файлов:

  • analyzer.jar - это содержит анализатор/тестовый код из выше
  • api.jar - это общий фронтальная код апи, например,интерфейсы
  • апи-осущ-v1.jar - это старая версия реализации
  • апи-осущ-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(); 
    } 
} 

Для запуска анализатора вы затем включить analyzer.jar и api.jar в пути к классам, но оставить из АНИ-осущ-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. Они просто должны быть загружены разными загрузчиками классов. –

+0

Интересно, у вас есть ссылка на хороший прочтение? Google провалил меня :( – amischiefr

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

} 

Дополнительные возможности могут быть добавлены позже с легкостью.

Смежные вопросы