.net:DLL 的依赖加载与 EXE 的依赖加载不同吗?
.net: is dependency loading for DLLs different than for EXEs?
我有一个很奇怪的问题。我做了一些非常疯狂的事情:我使用 IKVM 将使用 sbt-assembly 插件组装的 hadoop 库的胖 uber-jar 转换为 dll。我写了一个小测试程序,归结为以下内容:
var u = new java.net.URI("hdfs://my-namenode:8020/");
var fs = org.apache.hadoop.fs.FileSystem.get(u, new org.apache.hadoop.conf.Configuration());
foreach(var s in fs.listStatus(new org.apache.hadoop.fs.Path("/"))) {
Console.WriteLine(s.getPath().toString());
}
当我 运行 在控制台应用程序中使用我的 hadoop.dll 和所需的 IKVM dll 作为引用添加时,这会列出我的 HDFS 的内容。
但是,当我将这段代码准确地包装在一个 DLL 中,将 SAME 依赖项添加到该 DLL 并从我的控制台应用程序调用它时,我得到:
No FileSystem for scheme: hdfs
当我通过 fs.hdfs.impl
键在我的 Hadoop conf 中指定正确的 class 名称时,我得到一个 ClassNotFoundException
.
依赖项在可执行文件中的解析方式与在 DLL 中的解析方式不同吗?或者它可能是 IKVM 特定行为?
编辑:另一个奇怪的行为:当我在我的控制台应用程序中构建一次 FileSystem
然后在 DLL 中调用该方法时,它 运行s.
我自己找到了答案(再次...)
它不必按照 .net 处理依赖项加载的方式执行,但它是 IKVM(以及在这方面 Java)处理 classes 的动态加载的方式。
我仔细研究了 Hadoop 源代码,发现了以下内容:
private ClassLoader classLoader;
{
classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = Configuration.class.getClassLoader();
}
}
classLoader = Thread.currentThread().getContextClassLoader();
行在这里特别重要。我的控制台应用程序的上下文 class 加载程序是它的上下文 - 没有引用任何 Hadoop classes,因此 ClassNotFoundException
显式设置 fs.hdfs.impl
到 org.apache.hadoop.hdfs.DistributedFileSystem
.
幸好Configuration
class有个方法setClassLoader
,所以在构造配置的时候这样做:
var conf = new org.apache.hadoop.conf.Configuration();
conf.setClassLoader(conf.getClass().getClassLoader());
conf.set("fs.hdfs.impl", "org.apache.hadoop.hdfs.DistributedFileSystem");
有效!这是因为 conf.getClass().getClassLoader()
returns conf
上下文的 class 加载程序 - 即 hadoop.dll
转换后的 uber-jar 具有 class .
仍然有必要用 fs.XXXX.impl
显式声明文件系统 classes,因为自动文件系统解析机制如下所示:
private static void loadFileSystems() {
synchronized (FileSystem.class) {
if (!FILE_SYSTEMS_LOADED) {
ServiceLoader<FileSystem> serviceLoader = ServiceLoader.load(FileSystem.class);
for (FileSystem fs : serviceLoader) {
SERVICE_FILE_SYSTEMS.put(fs.getScheme(), fs.getClass());
}
FILE_SYSTEMS_LOADED = true;
}
}
如您所见,文件系统在这里解析:
ServiceLoader<FileSystem> serviceLoader = ServiceLoader.load(FileSystem.class);
此方法再次使用Thread.currentThread().getContextClassLoader()
,这意味着我的控制台应用程序没有hadoop classes。
所以,tl;dr:在创建 Configuration
之后,将其 ClassLoader 手动设置为 dll 的上下文 class 加载程序。
我有一个很奇怪的问题。我做了一些非常疯狂的事情:我使用 IKVM 将使用 sbt-assembly 插件组装的 hadoop 库的胖 uber-jar 转换为 dll。我写了一个小测试程序,归结为以下内容:
var u = new java.net.URI("hdfs://my-namenode:8020/");
var fs = org.apache.hadoop.fs.FileSystem.get(u, new org.apache.hadoop.conf.Configuration());
foreach(var s in fs.listStatus(new org.apache.hadoop.fs.Path("/"))) {
Console.WriteLine(s.getPath().toString());
}
当我 运行 在控制台应用程序中使用我的 hadoop.dll 和所需的 IKVM dll 作为引用添加时,这会列出我的 HDFS 的内容。
但是,当我将这段代码准确地包装在一个 DLL 中,将 SAME 依赖项添加到该 DLL 并从我的控制台应用程序调用它时,我得到:
No FileSystem for scheme: hdfs
当我通过 fs.hdfs.impl
键在我的 Hadoop conf 中指定正确的 class 名称时,我得到一个 ClassNotFoundException
.
依赖项在可执行文件中的解析方式与在 DLL 中的解析方式不同吗?或者它可能是 IKVM 特定行为?
编辑:另一个奇怪的行为:当我在我的控制台应用程序中构建一次 FileSystem
然后在 DLL 中调用该方法时,它 运行s.
我自己找到了答案(再次...)
它不必按照 .net 处理依赖项加载的方式执行,但它是 IKVM(以及在这方面 Java)处理 classes 的动态加载的方式。
我仔细研究了 Hadoop 源代码,发现了以下内容:
private ClassLoader classLoader;
{
classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = Configuration.class.getClassLoader();
}
}
classLoader = Thread.currentThread().getContextClassLoader();
行在这里特别重要。我的控制台应用程序的上下文 class 加载程序是它的上下文 - 没有引用任何 Hadoop classes,因此 ClassNotFoundException
显式设置 fs.hdfs.impl
到 org.apache.hadoop.hdfs.DistributedFileSystem
.
幸好Configuration
class有个方法setClassLoader
,所以在构造配置的时候这样做:
var conf = new org.apache.hadoop.conf.Configuration();
conf.setClassLoader(conf.getClass().getClassLoader());
conf.set("fs.hdfs.impl", "org.apache.hadoop.hdfs.DistributedFileSystem");
有效!这是因为 conf.getClass().getClassLoader()
returns conf
上下文的 class 加载程序 - 即 hadoop.dll
转换后的 uber-jar 具有 class .
仍然有必要用 fs.XXXX.impl
显式声明文件系统 classes,因为自动文件系统解析机制如下所示:
private static void loadFileSystems() {
synchronized (FileSystem.class) {
if (!FILE_SYSTEMS_LOADED) {
ServiceLoader<FileSystem> serviceLoader = ServiceLoader.load(FileSystem.class);
for (FileSystem fs : serviceLoader) {
SERVICE_FILE_SYSTEMS.put(fs.getScheme(), fs.getClass());
}
FILE_SYSTEMS_LOADED = true;
}
}
如您所见,文件系统在这里解析:
ServiceLoader<FileSystem> serviceLoader = ServiceLoader.load(FileSystem.class);
此方法再次使用Thread.currentThread().getContextClassLoader()
,这意味着我的控制台应用程序没有hadoop classes。
所以,tl;dr:在创建 Configuration
之后,将其 ClassLoader 手动设置为 dll 的上下文 class 加载程序。