Java:ExceptionInInitializerError创建使用自定义类加载器加载的Clojure编译类的新实例

时间:2014-11-07 18:05:31

标签: java reflection clojure classloader

我正在动态加载jar并尝试在jar中创建给定类的新实例。为了使事情变得更有趣,这个罐子是从Leiningen clojure项目编译而来的。我创建了一个自定义字节类加载器:

private static class ByteClassLoader extends ClassLoader {
    private final Map<String, byte[]> classDataMap;

    public ByteClassLoader() {
        this.classDataMap = new HashMap<>();
    }

    public void addClassData(String className, byte[] bytes) {
        classDataMap.put(className, bytes);
    }

    @Override
    protected Class<?> findClass(final String name) throws ClassNotFoundException {
        byte[] classData = classDataMap.get(name);
        if (classData != null)
            return defineClass(name, classData, 0, classData.length);
        return super.findClass(name);
    }
}

现在我从自定义类加载器中获取我感兴趣的类,并尝试创建一个新实例。

    Class<?> myClass = byteClassLoader.findClass("com.example.Foo");
    myClass.newInstance();

但这会抛出一个未找到文件的异常:

Caused by: java.io.FileNotFoundException: Could not locate some/namespace.class or some/namespace.clj on classpath: 
at clojure.lang.RT.load(RT.java:443)
at clojure.lang.RT.load(RT.java:411)
at clojure.core$load$fn__5018.invoke(core.clj:5530)
at clojure.core$load.doInvoke(core.clj:5529)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.lang.Var.invoke(Var.java:407)
at com.example.Foo.<clinit>(Unknown Source)
... 33 more

似乎对新实例的调用不了解自定义类加载器。这听起来像是对错误的正确理解吗?如何在此示例中实例化Foo?

1 个答案:

答案 0 :(得分:0)

创建的类<​​em> “知道”它的定义ClassLoader,JVM将使用定义的类加载器解析它的引用。但是,正如您从堆栈跟踪中看到的那样,clojure.lang.RT.load尝试以不兼容的方式解析资源。通过查看其代码,可以归结为following method

static public ClassLoader baseLoader(){
    if(Compiler.LOADER.isBound())
        return (ClassLoader) Compiler.LOADER.deref();
    else if(booleanCast(USE_CONTEXT_CLASSLOADER.deref()))
        return Thread.currentThread().getContextClassLoader();
    return Compiler.class.getClassLoader();
}

通过此代码,您可以获得问题的解决方案:

  1. 您可以将Compiler.LOADER的值设置为自定义类加载器。
  2. 如果Compiler.LOADER仍未绑定且RT.USE_CONTEXT_CLASSLOADER评估为true,则可以将线程的context loader设置为自定义类加载器。
  3. 最后但同样重要的是,如果您强制将类加载器作为整个clojure代码的定义类加载器,Compiler.class.getClassLoader()将返回您的自定义类加载器。