英文:
Classloading from package spawns NoClassDefFoundError
问题
以下是您提供的内容的翻译:
我有一个小程序,试图通过将类文件复制到特定的 ext
目录来实现插件功能。
该程序派生自 https://javaranch.com/journal/200607/Plugins.html,我尝试简化它并添加了一个目录扫描功能,以扫描原始代码缺少的包和目录。
在运行原始代码时,它可以正常工作。但是当我添加了目录和包扫描功能,并在演示包上进行测试时,它失败了。以下是示例。
系统接受动态加载的类文件作为插件的目录布局:
testpack-+
|
+---PluginDemo.java
|
+---PluginFunction.java
测试插件的目录布局:
b-+
|
+---Fibonacci.java
testpack-+
|
+---PluginFunction.java
带有自定义类加载器的 PluginDemo
代码:
package testpack;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.*;
public class PluginDemo extends ClassLoader {
static String pluginsDir = "ext";
static File basePluginDir = null;
File directory;
static List plugins;
public static void main(String args[]) {
PluginDemo demo = new PluginDemo();
basePluginDir = new File(System.getProperty("user.dir") + File.separator + pluginsDir);
demo.getPlugins(pluginsDir, "");
}
PluginDemo() {
plugins = new ArrayList();
}
protected void getPlugins(String directory, String parent) {
File dir = new File(System.getProperty("user.dir") + File.separator + directory);
if (dir.exists() && dir.isDirectory()) {
String[] files = dir.list();
for (int i = 0; i < files.length; i++) {
try {
// 允许递归地定位嵌套目录
String newTargetFile = System.getProperty("user.dir") + File.separator + directory + File.separator
+ files[i];
System.out.println("目标: " + newTargetFile);
File currentTarget = new File(newTargetFile);
if (currentTarget.isDirectory()) {
String newDirectoryTarget = directory + File.separator + files[i];
getPlugins(newDirectoryTarget, files[i]);
}
if (!files[i].endsWith(".class"))
continue;
String childFile = parent + File.separator + files[i].substring(0, files[i].indexOf("."));
Class c = loadClass(childFile);
Class[] intf = c.getInterfaces();
for (int j = 0; j < intf.length; j++) {
if (intf[j].getName().equals("PluginFunction")) {
PluginFunction pf = (PluginFunction) c.newInstance();
plugins.add(pf);
continue;
}
}
} catch (Exception ex) {
System.err.println(files[i] + " 文件不包含有效的 PluginFunction 类。");
ex.printStackTrace();
}
}
}
}
public Class loadClass(String name) throws ClassNotFoundException {
return loadClass(name, true);
}
public Class loadClass(String classname, boolean resolve) throws ClassNotFoundException {
try {
Class c = findLoadedClass(classname);
if (c == null) {
try {
c = findSystemClass(classname);
} catch (Exception ex) {
}
}
if (c == null) {
String filename = classname.replace('.', File.separatorChar) + ".class";
// 创建一个文件对象。将文件名相对于为此类加载器指定的目录进行解释。
File baseDir = new File(System.getProperty("user.dir"));
File f = new File(baseDir, PluginDemo.pluginsDir + File.separator + filename);
int length = (int) f.length();
byte[] classbytes = new byte[length];
DataInputStream in = new DataInputStream(new FileInputStream(f));
in.readFully(classbytes);
in.close();
c = defineClass(classname, classbytes, 0, length);
}
if (resolve)
resolveClass(c);
return c;
} catch (Exception ex) {
throw new ClassNotFoundException(ex.toString());
}
}
}
PluginFunction
接口代码:
package testpack;
public interface PluginFunction {
// 允许应用程序传入参数
public void setParameter(int param);
// 从插件检索结果
public int getResult();
// 返回此插件的名称
public String getPluginName();
// 可以调用以确定插件是否由于错误条件中止执行
public boolean hasError();
}
Fibonacci.java
代码:
package b;
import testpack.PluginFunction;
public class Fibonacci implements PluginFunction {
int parameter = 0;
boolean hasError = false;
public boolean hasError() {
return hasError;
}
public void setParameter(int param) {
parameter = param;
}
public int getResult() {
hasError = false;
return fib(parameter);
}
protected int fib(int n) {
if (n < 0) {
hasError = true;
return 0;
}
if (n == 0)
return 0;
else if (n == 1)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
public String getPluginName() {
return "Fibonacci";
}
}
带有错误输出的输出:
目标: C:\Users\Administrator\eclipse-workspace\TestPluginSystem\ext\b\Fibonacci.class
Exception in thread "main" java.lang.NoClassDefFoundError: b\Fibonacci (wrong name: b/Fibonacci)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
at testpack.PluginDemo.loadClass(PluginDemo.java:89)
at testpack.PluginDemo.loadClass(PluginDemo.java:65)
at testpack.PluginDemo.getPlugins(PluginDemo.java:47)
at testpack.PluginDemo.getPlugins(PluginDemo.java:40)
at testpack.PluginDemo.main(PluginDemo.java:19)
我需要帮助让这个带有包和目录扫描功能的类加载器正常工作。谢谢。
英文:
I have a small program that attempts to allow plugins via class files copied to a specific ext
directory.
The program is derived from https://javaranch.com/journal/200607/Plugins.html and I have attempted to simplify it and add on a directory scanning ability to scan packages and directories that the original code lacks.
When running the original code, it works. When I add on my directory and package scanning capability and test it on a demo package, it fails. Below are the samples.
The directory layout of the system accepting dynamically loaded class files as plugins:
testpack-+
|
+---PluginDemo.java
|
+---PluginFunction.java
The test plugin's directory layout:
b-+
|
+---Fibonacci.java
testpack-+
|
+---PluginFunction.java
The PluginDemo
code with custom class loader:
package testpack;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.*;
public class PluginDemo extends ClassLoader {
static String pluginsDir = "ext";
static File basePluginDir = null;
File directory;
static List plugins;
public static void main(String args[]) {
PluginDemo demo = new PluginDemo();
basePluginDir = new File(System.getProperty("user.dir") + File.separator + pluginsDir);
demo.getPlugins(pluginsDir, "");
}
PluginDemo() {
plugins = new ArrayList();
}
protected void getPlugins(String directory, String parent) {
File dir = new File(System.getProperty("user.dir") + File.separator + directory);
if (dir.exists() && dir.isDirectory()) {
String[] files = dir.list();
for (int i = 0; i < files.length; i++) {
try {
// Allows recursive targetting of nested directories
String newTargetFile = System.getProperty("user.dir") + File.separator + directory + File.separator
+ files[i];
System.out.println("Targetting: " + newTargetFile);
File currentTarget = new File(newTargetFile);
if (currentTarget.isDirectory()) {
String newDirectoryTarget = directory + File.separator + files[i];
getPlugins(newDirectoryTarget, files[i]);
}
if (!files[i].endsWith(".class"))
continue;
String childFile = parent + File.separator + files[i].substring(0, files[i].indexOf("."));
Class c = loadClass(childFile);
Class[] intf = c.getInterfaces();
for (int j = 0; j < intf.length; j++) {
if (intf[j].getName().equals("PluginFunction")) {
PluginFunction pf = (PluginFunction) c.newInstance();
plugins.add(pf);
continue;
}
}
} catch (Exception ex) {
System.err.println("File " + files[i] + " does not contain a valid PluginFunction class.");
ex.printStackTrace();
}
}
}
}
public Class loadClass(String name) throws ClassNotFoundException {
return loadClass(name, true);
}
public Class loadClass(String classname, boolean resolve) throws ClassNotFoundException {
try {
Class c = findLoadedClass(classname);
if (c == null) {
try {
c = findSystemClass(classname);
} catch (Exception ex) {
}
}
if (c == null) {
String filename = classname.replace('.', File.separatorChar) + ".class";
// Create a File object. Interpret the filename relative to the
// directory specified for this ClassLoader.
File baseDir = new File(System.getProperty("user.dir"));
File f = new File(baseDir, PluginDemo.pluginsDir + File.separator + filename);
int length = (int) f.length();
byte[] classbytes = new byte[length];
DataInputStream in = new DataInputStream(new FileInputStream(f));
in.readFully(classbytes);
in.close();
c = defineClass(classname, classbytes, 0, length);
}
if (resolve)
resolveClass(c);
return c;
} catch (Exception ex) {
throw new ClassNotFoundException(ex.toString());
}
}
}
The PluginFunction
interface code:
package testpack;
public interface PluginFunction {
// let the application pass in a parameter
public void setParameter (int param);
// retrieve a result from the plugin
public int getResult();
// return the name of this plugin
public String getPluginName();
// can be called to determine whether the plugin
// aborted execution due to an error condition
public boolean hasError();
}
The Fibonacci.java
code:
package b;
import testpack.PluginFunction;
public class Fibonacci implements PluginFunction {
int parameter = 0;
boolean hasError = false;
public boolean hasError() {
return hasError;
}
public void setParameter (int param) {
parameter = param;
}
public int getResult() {
hasError = false;
return fib(parameter);
}
protected int fib (int n) {
if (n < 0) {
hasError = true;
return 0;
}
if (n == 0)
return 0;
else if (n == 1)
return 1;
else
return fib(n-1) + fib(n-2);
}
public String getPluginName() {
return "Fibonacci";
}
}
The output with errors:
Targetting: C:\Users\Administrator\eclipse-workspace\TestPluginSystem\ext\b\Fibonacci.class
Exception in thread "main" java.lang.NoClassDefFoundError: b\Fibonacci (wrong name: b/Fibonacci)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
at testpack.PluginDemo.loadClass(PluginDemo.java:89)
at testpack.PluginDemo.loadClass(PluginDemo.java:65)
at testpack.PluginDemo.getPlugins(PluginDemo.java:47)
at testpack.PluginDemo.getPlugins(PluginDemo.java:40)
at testpack.PluginDemo.main(PluginDemo.java:19)
I would need help to get this package and directory scanning capable classloader working. Thanks.
答案1
得分: 1
看到错误和方法[ClassLoader.defineClass] 1,我认为name参数必须使用。
作为包分隔符,而不是/
或\
。
在您的代码中,在getPlugins中,childFile是使用File.separator构造的。
String childFile = parent + File.separator + files[i].substring(0, files[i].indexOf(“。”));
Class c = loadClass(childFile);
英文:
Looking at the error and method ClassLoader.defineClass, I think the name parameter must have .
as package separators, not /
or \
.
In your code in getPlugins the childFile is constructed using File.separator
String childFile = parent + File.separator + files[i].substring(0, files[i].indexOf("."));
Class c = loadClass(childFile);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论