类加载器:如何从另一个项目中加载类

4

我想使用ClassLoader从另一个项目访问一个类。我该如何指定该类的路径并获取该类文件?

我希望通过代码实现这一点,因为我的应用程序将加载许多不同的类文件,而不同类的路径将不断变化。

我正在使用CustomClassLoader加载类文件,但仅当它们在当前项目中而不是在其他项目中时才会加载。

import java.io.FileInputStream;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;

public class CustomClassLoader extends ClassLoader {

String repoLocation = "C:/TempBINfolder/bin/";

public CustomClassLoader() {
}

public CustomClassLoader(ClassLoader parent) {
    super(parent);
}

@Override
protected Class<?> findClass(final String name)
        throws ClassNotFoundException {

    AccessControlContext acc = AccessController.getContext();

    try {
        return (Class) AccessController.doPrivileged(
                new PrivilegedExceptionAction() {

                    public Object run() throws ClassNotFoundException {

                        FileInputStream fi = null;
                        try {

                            String path = name.replace('.', '/');
                            fi = new FileInputStream(repoLocation + path
                                    + ".class");
                            ByteArrayOutputStream baos = new ByteArrayOutputStream();
                            byte[] buffer = new byte[8192]; // a big chunk
                            int read;
                            while ((read = fi.read(buffer)) > 0)
                                baos.write(buffer, 0, read);
                            byte[] classBytes= baos.toByteArray();

                            return defineClass(name, classBytes, 0,
                                    classBytes.length);
                        } catch (Exception e) {
                            throw new ClassNotFoundException(name);
                        }
                    }
                }, acc);
    } catch (java.security.PrivilegedActionException pae) {
        return super.findClass(name);
    }
}
}

调用类
for (Class singleClass : listOfClasses) {
            try {

                ClassLoader classLoader = new CustomClassLoader(ClassLoader.getSystemClassLoader());
                Class stringClass = null;
                try {
                    stringClass = classLoader.loadClass(singleClass.getName());
                } catch (ClassNotFoundException ex) {
                    Logger.getLogger(CompilerForm.class.getName()).log(Level.SEVERE, null, ex);
                }
                try {
                    stringClass.newInstance();
                } catch (InstantiationException ex) {
                    Logger.getLogger(CompilerForm.class.getName()).log(Level.SEVERE, null, ex);
                } catch (IllegalAccessException ex) {
                    Logger.getLogger(CompilerForm.class.getName()).log(Level.SEVERE, null, ex);
                }


                Class cls = Class.forName(stringClass.getName());

如果我尝试执行Class cls = Class.forName(stringClass.getPackage()+"."+stringClass.getName());,则包为null
编辑:以下对我有效。
URL classUrl;
classUrl = new URL("file:///"+ccl.getRepoLocation());    //This is location of .class file
URL[] classUrls = {classUrl};
URLClassLoader ucl = new URLClassLoader(classUrls);
Class cls = ucl.loadClass(stringClass.getName());    // Current .class files name

这个是怎么使用的?你没有提及这些项目是如何打包的,或者为什么首先要使用自定义的ClassLoader。通常情况下,至少需要将每个项目打成jar包,并将依赖的jar包放在其他jar包的类路径中(在这种情况下,你应该能够使用parent.getResourceAsStream("...")来获取它)。 - Vala
3个回答

2

2
那段代码看起来很不错(我自己很久以前也做过类似的事情)。但是有一个小错误:
如果你执行

byte[] classBytes = new byte[fi.available()];
fi.read(classBytes);

你只会读取可用的字节数,而且不会阻塞。也就是说,你并没有读取整个文件。实际上,read方法不能保证完整的字节缓冲区被读取。

尝试这样做:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[8192]; // a big chunk
int read;
while ((read = fi.read(buffer)) > 0)
   baos.write(buffer, 0, read);
byte[] bytesClass = baos.toByteArray();

或者使用来自Apache的Streams.copy。这是一个方便的方法,可以完成相同的任务。

包定义

ClassLoader有一个definePackage方法。我打赌你应该为每个新包调用该方法。否则,ClassLoader没有办法定义一个包,除非从完整的类名中定义,而似乎这是不够的。

所以代码变成了这样:

// being package the name of the package for the new class
// being definedPackages a Set<String> member of the classloader

if (!this.definedPackages.contains(package)) {
   definePackage(package,"","","","","","",null);
   this.definedPackages.add(package);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[8192]; // a big chunk
int read;
while ((read = fi.read(buffer)) > 0)
   baos.write(buffer, 0, read);
byte[] bytesClass = baos.toByteArray();

完全不是。那么,问题不在于加载类,而在于检索相应的包,对吧?(我误解了这一点) - helios
是的,我能获取到这个类,但是问题在于如何获取该类所在的包! - Hip Hip Array
好的,我在我的帖子中添加了definePackage...看起来它很相关 :) - helios
但我是在我的CustomClassLoader中调用它,因此它应该返回类路径,对吗? - Hip Hip Array
你必须调用definePackage(至少如果之前没有定义包)然后调用defineClass。例如:调用findClass("a.b.C"),然后调用definePackage("a.b", ...),最后调用defineClass(...)。因此,返回的Class对象将附加到与刚刚进行的definePackage调用相对应的新Package对象上。请记住,不要为同一个包调用两次definePackage。 - helios
我正在研究这个,可以用这种方式实现,如果我用这种方式可能更有效率,但我自己做不到,所以我决定使用URLClassLoader - Hip Hip Array

0

感谢上面的代码帮助了我。

主题:在两个不同位置调用相同的类

我有一个类,称为Abc,在classpath jar文件中,并且动态地我在本地目录中生成了相同的类Abc,其中包含一些代码更改。

我需要创建实例并使用本地目录中的Abc类,下面是工作代码:

class CustomClassLoader extends ClassLoader {

String repoLocation = "./";
//C:/TempBINfolder/bin/
 CustomClassLoader() {
}

 CustomClassLoader(ClassLoader parent) {
    super(parent);
}

@Override
protected Class<?> findClass(final String name)   throws ClassNotFoundException {

    AccessControlContext acc = AccessController.getContext();

    try {
        return (Class) AccessController.doPrivileged(
                new PrivilegedExceptionAction() {

                    public Object run() throws ClassNotFoundException {

                        FileInputStream fi = null;
                        try {

                            String path = name.replace('.', '/');
                            fi = new FileInputStream(repoLocation + path+ ".class");
                            ByteArrayOutputStream baos = new ByteArrayOutputStream();
                            byte[] buffer = new byte[8192]; // a big chunk
                            int read;
                            while ((read = fi.read(buffer)) > 0)
                                baos.write(buffer, 0, read);
                            byte[] classBytes= baos.toByteArray();

                            return defineClass(name, classBytes, 0,
                                    classBytes.length);
                        } catch (Exception e) {
                            throw new ClassNotFoundException(name);
                        }
                    }
                }, acc);
    } catch (java.security.PrivilegedActionException pae) {
        return super.findClass(name);
    }
}
}

调用 CustomClassLoader 类,
    ClassLoader classLoader = new CustomClassLoader(ClassLoader.getSystemClassLoader());
    Class stringClass = (new CustomClassLoader(ClassLoader.getSystemClassLoader())).findClass(packageName+"."+javaFileName);
    Object  t = (Object) stringClass.newInstance();

谢谢,Murwath


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接