如何从数据库加载java类?

时间:2011-03-16 18:04:28

标签: java

使用以下源代码:

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

我可以使用:

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

实例化此类,但我必须将此源文件(Test.java)放在类路径/util/abc/

我想从数据库动态加载此类(将源代码存储为stringbinary

这可能吗?

2 个答案:

答案 0 :(得分:21)

假设您已经编译了该类,您可以创建一个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.
    }
}

如果数据库只包含源代码,则必须先编译它 - 查看Java编译器API,了解如何在没有任何文件的情况下执行此操作。

请注意,只要类加载器处于活动状态,以这种方式加载的类将保持活动状态,因此您需要一个新的类加载器才能在发生更改时重新加载该类。

另外,如果你想通过除反射之外的其他方式与类交互,你最好让它实现一些接口(它本身在你的类路径中),并让应用程序类加载器成为数据库类的父类加载器。

啊,以及如何加载:

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,因此它们只能通过接口(或反射)相互协作。

对于动态编译,您应该查看Java Compiler API,如 dcn 的答案中所述。但是我认为最好将编译放在数据库中,而不是那些将它们拉出来的人。

答案 1 :(得分:5)

如果要将源代码存储在DB中,可以使用Java 6 Compiler API在运行时编译它。有关示例,请参阅here

为了在运行时加载类,如果可以使用URL指定字节码的位置,则可以使用URLClassLoader,或使用ClassLoader.defineClass并将字节码作为字节数组提供。

无论哪种方式你应该注意,为了使用动态加载的类,它应该实现一个在编译时已知的接口。