Вместо того, чтобы использовать DLL как таковую, создается впечатление, что требуется какая-то архитектура плагинов.
Одна из причин, по которой я бы не рекомендовал использовать DLL, если в этом нет необходимости, заключается в том, что для связывания кода Java с собственным кодом потребуется использование собственного интерфейса Java (JNI), что, вероятно, потребует больше усилий, чем чисто Java решение.
Одним из относительно простых способов сделать это является использование отражений возможностей Java.
Исходя из предоставленной информации, я бы, вероятно, согласился со следующим:
- Определить интерфейс для выходного формата.
- Создайте класс Java, реализующий интерфейс.
- Получить класс можно по classpath .
- Динамически загружать класс с помощью отражения. (С помощью метода
Class.newInstance
можно создавать объекты из файлов class
, загруженных ClassLoader
.)
С этими шагами можно было бы реализовать упрощенный плагин, который не потребовал бы полной перестройки, когда требуется поддержка нового формата.
Шаг 1: определить интерфейс
Допустим, мы получили интерфейс, подобный следующему:
public interface Outputter {
public void write(Data d);
}
Шаг 2. Создание класса реализации
Затем мы создадим класс реализации.
public class TextOutputter {
public void write(Data d) {
// ... output data to text
}
}
Затем, скомпилировав вышесказанное, мы получим файл class
с именем TextOutputter.class
.
Шаг 3: Сделать класс доступным из classpath
При запуске основного приложения нам потребуется указанная выше TextOutputter.class
в classpath . Обычно нужно сообщить JVM список мест, которые следует рассматривать как путь к классам, и он должен включать в себя указанный выше файл class
.
Как только это будет сделано, мы сможем загрузить вышеуказанный класс, используя отражение.
Шаг 4: Динамически загрузить класс с помощью отражения
Теперь, когда мы действительно хотим загрузить вышеуказанный класс, мы сделаем что-то вроде следующего:
// Note: We load the class by specifying the fully-qualified class name!
Class<?> clazz = Class.forName("TextOutputter");
// Then, we instantiate the class.
// Note that the following method will call the no-argument constructor.
Outputter outputter = clazz.newInstance();
// Now, we can give data to the TextOutputter object that we loaded dynamically.
outputter.write(...);
Метод Class.forName
используется для попытки найти класс TextOutputter
из значения по умолчанию ClassLoader
. Как только мы получим класс как представление Class
, мы сможем создать экземпляр объекта этого класса.
Создание объекта можно выполнить с помощью метода Class.newInstance
. Если нужно использовать что-то иное, чем конструктор без аргументов, Constructor
класса нужно будет получить, продолжая создавать экземпляр объекта оттуда.
Объект создается посредством отражения, затем помещается в переменную Outputter
, поэтому метод write
можно вызывать для TextOutputter
.
Добавление большего числа форматов повлечет за собой описанный выше процесс, но изменение полного имени класса (например, для String
, FQCN - java.lang.String
) - это все, что необходимо для загрузки другого класса.
В двух словах, это то, что нужно для динамической загрузки class
файлов и использования их из вашего приложения.
(Как примечание, на самом деле я не скомпилировал приведенный выше код, поэтому здесь и там могут быть некоторые ошибки, но я надеюсь, что смогу проиллюстрировать процесс, который он предпримет.)