我需要使用自定义ClassLoader来加载第三方驱动程序。计划是,驱动程序只是共享jar档案,但带有.dar后缀(驱动程序存档)。然后将这些存档简单地添加到类路径中,而jvm忽略此文件扩展名并处理dar文件(如资源)(这意味着忽略其中的类文件)。
现在我的问题是,我的类加载器在类文件异常中的常量池中给了我一个非法的UTF8字符串。我怎么能绕过那个?
public class DriversClassLoader extends ClassLoader {
public DriversClassLoader() {
super(ClassLoader.getSystemClassLoader());
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if( name.startsWith("java.")) {
return super.loadClass(name, resolve);
} else {
// see if we have already loaded the class.
Class<?> c = findLoadedClass(name);
if (c != null) return c;
try {
URL driversUrl = super.getResource("drivers.dar").toURI().toURL();
System.out.println(driversUrl);
JarInputStream jis = new JarInputStream(driversUrl.openConnection().getInputStream());
ZipEntry entry;
while ((entry = jis.getNextEntry()) != null) {
if (entry.getName().matches(name)) {
System.out.println("loading: " + entry);
byte[] cBytes = new byte[(int) entry.getSize()];
jis.read(cBytes, 0, cBytes.length);
// this is where I get the exception
c = defineClass(name, cBytes, 0, cBytes.length);
if (resolve) resolveClass(c);
return c;
}
}
} catch (Exception e) {
throw new ClassNotFoundException("loading of class " + name + " failed", e);
}
// else delegate to parent
return super.loadClass(name, resolve);
/*
URL res = getResource(name);
if (res == null) throw new ClassNotFoundException("Class " + name + "not found in:");
try {
byte[] cBytes = IOUtils.toByteArray(new InputStreamReader(res.openStream()));
c = defineClass(name, cBytes, 0, cBytes.length);
if (resolve) resolveClass(c);
return c;
} catch (Exception e) {
throw new ClassNotFoundException("loading of class " + name + " failed", e);
}
// delegate to parent
return super.loadClass(name, resolve);*/
}
}
@Override
public URL getResource(String name) {
try {
URL driversUrl = super.getResource("drivers.dar").toURI().toURL();
System.out.println(driversUrl);
JarInputStream jis = new JarInputStream(driversUrl.openConnection().getInputStream());
ZipEntry entry;
while ((entry = jis.getNextEntry()) != null) {
if (entry.getName().matches(name)) {
System.out.println(entry);
return new URL("jar:" + driversUrl + "!/" + entry);
}
}
} catch (Exception e) {
e.printStackTrace();
}
// delegate to parent
return super.getResource(name);
}
@Override
public Enumeration<URL> getResources(String name) throws IOException {
//super.get
return super.getResources(name);
}
}
然后我只是
Class<?> aClass = Class.forName("a.class", false, new DriverClassLoader());
System.out.println("aclass = " + aClass);
编辑1: 但该文件包含有效的类:
java -cp ../rangeCache/src/main/resources/drivers.dar pkg.test.Test
从dar jar输出Hello
虽然这不是:
java -cp target/libs/range-cache-302-SNAPSHOT.jar:drivers/ com.data.cache.range.drivers.DriversClassLoader
jar:file:/home/rangeCache/target/libs/range-cache-302-SNAPSHOT.jar!/drivers.dar
loading: com/data/cache/range/drivers/DatastaxCassandraDriver.class
Exception in thread "main" java.lang.ClassFormatError: Illegal UTF8 string in constant pool in class file com/data/cache/range/drivers/DatastaxCassandraDriver/class
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
at com.mindbusters.data.cache.range.drivers.DriversClassLoader.loadClass(DriversClassLoader.java:40)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at com.data.cache.range.drivers.DriversClassLoader.main(DriversClassLoader.java:105)
答案 0 :(得分:2)
您从ZipEntry
读取字节的代码已损坏。
您首先不应该依赖ZipEntry.getSize()
,因为-1
如果不知道可能会被返回(请参阅Javadocs)。
然后InputStream.read(byte[], int , int)
可能会读取少个字节而不是请求的字节数。
您应该在循环中读取字节,直到返回-1
或使用IOUtils.toByteArray
,就像您已经执行过一次。