Как загрузить java-класс из базы данных?

Со следующим исходным кодом:

package util.abc;
public class Test{
    public String out(){
        return "Hello World!";
    }
}

Я могу использовать:

Class c = Class.forName("util.abc.Test");

чтобы создать экземпляр этого класса, но я должен поместить этот исходный файл (Test.java) в classpath /util/abc/

Я хочу динамически загрузить этот класс из базы данных (сохранить исходный код в виде string или binary)

Это возможно?

Ответ 1

Предполагая, что вы уже скомпилировали класс, вы можете создать DatabaseClassLoader, который загружает класс из базы данных.

public class DatabaseClassLoader extends ClassLoader {

    public DatabaseClassLoader(ClassLoader parent, ... /* connection to database */ ) {
       super(parent);
       // store the connection
    }

    public Class findClass(String name) {
       byte[] data = loadDataFromDatabase(name);
       return defineClass(name, data, 0, data.length);
    }
    private byte[] loadDataFromDatabase(String name) {
        // this is your job.
    }
}

Если база данных содержит только исходный код, вам придется сначала ее скомпилировать - загляните в API-интерфейс компилятора Java, как это сделать без каких-либо файлов.

Обратите внимание: класс, загруженный таким образом, останется в живых до тех пор, пока классный загрузчик жив, поэтому вам понадобится новый загрузчик классов, чтобы перезагрузить класс в случае изменений.

Кроме того, если вы хотите взаимодействовать с классом другими способами, чем с рефлексией, вам лучше позволить ему реализовать некоторый интерфейс (который сам находится в вашем пути к классу) и позволить загрузчику класса приложения быть родителем класса базы данных загрузчик.

Ah, и как загрузить:

Class<?> c = Class.forName("util.abc.Test", myClassLoader);

или непосредственно

Class<?> c = myClassLoader.loadClass("util.abc.Test");

Вот метод, который создает объекты вашего интерфейса (фактически любого интерфейса):

public <X> X getImplementingObject(Class<X> interfaceClass, String className)
   throws ClassNotFoundException, IllegalAccessException, InstantiationException
{
    ClassLoader loader = new DatabaseClassLoader(interfaceClass.getClassLoader(), ...);
    Class<?> cl = loader.loadClass(className);
    Class<? extends X> c = cl.asSubclass(interfaceClass);
    return c.newInstance();
}

(Ему нужен класс, чтобы иметь конструктор без аргументов, который, конечно, не генерирует никаких исключений (если это так, вы также получите это исключение).

Это создает новый ClassLoader для каждого такого класса, поэтому они могут взаимодействовать только друг с другом с помощью интерфейса (или отражения).

Для компиляции "на лету" вы должны посмотреть на API компилятора Java, как указано в ответе от dcn. Но я думаю, что было бы лучше сделать компиляцию на стороне, которая помещает классы в базу данных, чем сторона, которая вытаскивает их.

Ответ 2

Если вы хотите сохранить исходный код в своем БД, вы можете использовать API компилятора Java 6 для его компиляции во время выполнения. См. здесь для примера.

Чтобы загружать классы во время выполнения, вы можете использовать URLClassLoader, если вы можете указать местоположение байт-кода с URL-адресом или использовать ClassLoader.defineClass и поставьте байт-код в виде массива байтов.

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