.net:是DLL与EXE不同的依赖项加载
本文关键字:依赖 加载 EXE DLL net | 更新日期: 2023-09-27 18:19:49
我有一个非常奇怪的问题。我做了一些非常疯狂的事情:我使用IKVM将我用sbt汇编插件组装的一个庞大的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中,向该DLL添加相同的依赖项并从控制台应用程序中调用时,我得到:
No FileSystem for scheme: hdfs
当我通过fs.hdfs.impl
键在Hadoop conf中指定正确的类名时,我会得到一个ClassNotFoundException
。
可执行文件中的依赖项解析方式与DLL中的不同吗?或者这可能是IKVM特有的行为?
编辑:另一个奇怪的行为:当我在控制台应用程序中构造FileSystem
一次,然后在DLL中调用该方法时,它就会运行。
我自己找到了答案(再次…)
它不一定要做.net如何处理依赖项加载,而是IKVM(以及Java)如何处理类的动态加载。
我挖掘了Hadoop的源代码,发现了以下部分:
private ClassLoader classLoader;
{
classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = Configuration.class.getClassLoader();
}
}
线路classLoader = Thread.currentThread().getContextClassLoader();
在这里特别令人感兴趣。控制台应用程序的上下文类加载器是它的上下文,不引用任何Hadoop类,因此在显式设置fs.hdfs.impl
为org.apache.hadoop.hdfs.DistributedFileSystem
时为ClassNotFoundException
。
幸运的是,Configuration
类有一个方法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()
返回conf
的上下文的类加载器,即具有该类的hadoop.dll
转换的uber jar。
仍然需要用fs.XXXX.impl
显式地声明文件系统类,因为自动文件系统解析机制如下所示:
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类。
所以,tl;dr:创建Configuration
后,手动将其ClassLoader设置为dll的上下文类加载器。