【发布时间】:2017-12-06 23:48:01
【问题描述】:
我一直在使用 Java 中的简单自定义类加载器,到目前为止,对于非模块相关的类,一切都按预期工作。但是,即使与模块相关的find*() 方法已被重载,我似乎也找不到使用我的类加载器从模块加载类的任何方法。我试图做的是从模块“HelloModularWorld”加载一个类并运行它。但是,当我指定包所在的目录时,它会“正常”加载并报告为在未命名的模块中。我错过了什么?
类加载器只是从文件系统的其他地方加载类,没什么特别的。
类加载器实现:
package arch.classloaders;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
public class ModifiableClassLoader extends ClassLoader {
private File searchPath;
//...other ctors...
public ModifiableClassLoader(File path, String name, ClassLoader parent) {
super(name, parent); //Delegate to parent classloader as required.
if (!path.isDirectory()) throw new IllegalArgumentException("Path must be a directory");
searchPath = path;
}
public void setPath(File newPath) {...}
public File getPath() {...}
//Responsible for actual loading
public Class<?> findClass(String binName) throws ClassNotFoundException {
File classfile = new File(searchPath.getPath() + File.separator
+ binName.replace('.', File.separatorChar) + ".class");
byte[] buf;
FileInputStream fis;
try {
fis = new FileInputStream(classfile);
buf = fis.readAllBytes();
fis.close();
} catch (IOException e) {
throw new ClassNotFoundException("Error in defining " + binName + " in " + searchPath.getPath(),e);
}
return defineClass(binName, buf, 0, buf.length);
}
//Modules are expected to be in a folder with the same name as the module.
//e.g. module hellomodularworld is expected to be in a folder
//<SEARCHPATH>/<MODNAME>/
//NOTE: Module-overloaded methods return null rather than throw when class isn't found.
public Class<?> findClass(String modName, String binName) {
if (null == modName) {
try {
return findClass(binName);
} catch (ClassNotFoundException e) {
return null;
}
}
File classfile = new File(searchPath.getPath() + File.separator
+ modName + File.separator
+ binName.replace('.', File.separatorChar) + ".class");
byte[] buf;
FileInputStream fis;
try {
fis = new FileInputStream(classfile);
buf = fis.readAllBytes();
fis.close();
} catch (IOException e) {
return null;
}
return defineClass(binName, buf, 0, buf.length);
}
//Non-module
public URL findResource(String resPath) {...}
//Module version
public URL findResource(String modName, String resPath) throws IOException {...}
//Enumeration version; does nothing.
public java.util.Enumeration<URL> findResources(String resPath) {...}
}
测试代码:
public class Test {
public static void main(String[] args) {
ModifiableClassLoader mcl = new ModifiableClassLoader(
new File("C:\\Users\\archd\\Desktop\\"));
try {
Class<?> clazz = mcl.loadClass(
"hellomodularworld/com.archdukeliamus.hellomodularworld.HelloWorld"
);
java.lang.reflect.Method mth = clazz.getMethod("main", String[].class);
mth.invoke(null,new Object[] {null});
System.out.println(clazz.getModule().getName());
} catch (...) {
//omitted
}
}
在线Class<?> clazz = mcl.loadClass("hellomodularworld/com.archdukeliamus.hellomodularworld.HelloWorld");我试过了:
- “hellomodularworld/com.archdukeliamus.hellomodularworld.HelloWorld” - 不工作。 ClassNotFoundException。
- “hellomodularworld.com.archdukeliamus.hellomodularworld.HelloWorld” -
NoClassDefFoundException。
com/archdukeliamus/hellomodularworld/HelloWorld (wrong name: hellomodularworld/com/archdukeliamus/hellomodularworld/HelloWorld) - “com.archdukeliamus.hellomodularworld.HelloWorld” - 按预期工作(类加载文件夹已适当更改)但使用未命名的模块。
编辑:模块信息.java
module hellomodularworld {
}
测试类不在任何模块中。 (我不完全确定为什么这很重要,我应该得到一个例外,即我没有得到“这个包没有导出”的效果。)
编辑 2:
修改模块以包含exports com.archdukeliamus.hellomodularworld;。结果没有变化。
【问题讨论】:
-
我还没有真正使用过 Java 9 模块,但我很确定你不能将那种字符串传递给 loadClass。见here。
-
虽然这确实澄清了二进制名称中似乎不允许使用模块,但它仍然引出了类加载器如何获取模块开头的问题。 ClassLoader 页面中似乎没有指定这一点。
-
我要求这样做的原因是 如果请求加载其包未在任何已知模块中定义的类型,则模块系统将尝试从 类路径。如果成功,则该类型被认为是称为未命名模块的特殊模块的成员,以确保每个类型都与某个模块相关联
-
代码片段只是一个类加载器,用于加载部署为模块的类。您创建配置并将模块映射到类加载器的代码在哪里,您必须在某个地方调用 ModuleLayer 的 defineModule,对吧?
-
顺便说一句,你为什么要绕道
FileInputStream?您可以简单地使用byte[] buf = Files.readAllBytes(searchPath.toPath().resolve(binName.replace('.', File.separatorChar) + ".class"));。从 Java 7 开始。
标签: java classloader java-9 java-platform-module-system java-module