【发布时间】:2018-08-18 11:37:06
【问题描述】:
想法
我在byte[] 中有一个罐子 (postgresql-9.4.1208.jre7.jar)。我想在运行时加载、连接和运行一些基本的 SQL 命令。
实施
因此我创建了一个新的类加载器:
public class JarClassloader extends ClassLoader {
public interface DriverProblemReporter {
void reportDriverProblem(String name, Throwable e);
}
private final byte[] driverdata;
private final DriverProblemReporter problemReporter;
public JarClassloader(byte[] jar, String drivername, DriverProblemReporter reporter) {
super(ClassLoader.getSystemClassLoader());
this.problemReporter = reporter;
try {
JarInputStream jis = new JarInputStream(new ByteArrayInputStream(jar));
JarEntry entry = jis.getNextJarEntry();
while (entry != null) {
handleEntry(entry, jis);
entry = jis.getNextJarEntry();
}
} catch (IOException e) {
e.printStackTrace();
}
this.driverdata = jar;
}
private void handleEntry(JarEntry entry, JarInputStream jis) {
if (!entry.isDirectory()) {
ByteArrayOutputStream baos;
try {
baos = new ByteArrayOutputStream();
IOUtils.copy(jis, baos);
baos.flush();
} catch (IOException e) {
problemReporter.reportDriverProblem(entry.getName(), e);
return;
}
try {
defineClass(baos.toByteArray(), 0, baos.size());
} catch (LinkageError e) {
problemReporter.reportDriverProblem(entry.getName(), e);
}
}
}
}
Jar 加载成功,我可以获取 Driver 的实例。
兴趣点
在调用连接到数据库时出现此错误:
java.lang.NoClassDefFoundError: org/postgresql/hostchooser/HostRequirement$1
at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:107)
at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:66)
at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:215)
at org.postgresql.Driver.makeConnection(Driver.java:406)
at org.postgresql.Driver.connect(Driver.java:274)
在堆栈跟踪中,我看到 org.postgresql.Driver 的工作实例正在寻找一个名为 org/postgresql/hostchooser/HostRequirement$1 的类。
假设
我的JarClassloader 不加载匿名嵌套类。
问题
我应该怎么做才能成功加载jar中的所有类?
【问题讨论】:
-
为什么你的 jar 是字节数组?那个字节数组是从哪里来的?如果它来自文件,则不应将其加载到字节数组中;您应该将文件的 URL 传递给 URLClassLoader。
-
@VGR 它不是来自文件。我可以编写一个 servlet,但应用程序服务器(tomcat)没有 http 连接器来服务 http 请求。我也可以写一个 url-protocol-handler,但这可能会带来安全问题。
-
我会使用Files.createTempFile(null, ".jar"),然后是Files.write(tempFile, driverdata),然后是
new URLClassLoader(new URL[] { tempFile.toUri().toURL() })。换句话说,将字节写入一个临时的 .jar 文件,并从该文件中创建一个标准的 ClassLoader。 -
@VGR 由于磁盘已满或权限无效,临时文件可能会被更改或不允许创建,并且可能会在进程终止后保留空间。同样通过设计,它将单一真实来源缓存到基于文件的缓存中。从技术上讲,jar 文件是真相的来源,通过设计,字节数组是真相的来源。单一事实来源要求技术来源和设计来源相同。
-
你的推理可以应用于文件的所有使用。
标签: java jar classloader