.net:是不同于EXE的DLL的依赖加载?

时间:2015-10-30 08:59:11

标签: c# .net dll ikvm

我有一个非常奇怪的问题。我做了一些非常疯狂的事情:我使用IKVM将我使用sbt-assembly插件组装的胖的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中指定正确的类名时,我得到ClassNotFoundException

可执行文件中的依赖项是否在DLL中的解析方式不同,还是可能是IKVM特定的行为?

编辑:另一个奇怪的行为:当我在我的控制台应用程序中构造FileSystem一次,然后在DLL中调用该方法时,它就会运行。

1 个答案:

答案 0 :(得分:1)

我自己找到了答案(再次......)

它不必如何处理.net处理依赖性加载,但它是IKVM(以及Java方面)处理类的动态加载的方式。

我挖掘了Hadoop源代码,发现了以下内容:

private ClassLoader classLoader;
{
  classLoader = Thread.currentThread().getContextClassLoader();
  if (classLoader == null) {
    classLoader = Configuration.class.getClassLoader();
  }
}

这里特别感兴趣的是classLoader = Thread.currentThread().getContextClassLoader();这一行。我的控制台应用程序的上下文类加载器是它的上下文 - 没有引用任何Hadoop类,因此ClassNotFoundException明确地将fs.hdfs.impl设置为org.apache.hadoop.hdfs.DistributedFileSystem

幸运的是,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的上下文类加载器。