Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Java代码中测试Class.forName调用?_Java_Unit Testing_Classloader - Fatal编程技术网

如何在Java代码中测试Class.forName调用?

如何在Java代码中测试Class.forName调用?,java,unit-testing,classloader,Java,Unit Testing,Classloader,最近,我一直在玩java中的类加载器,试图测试使用自定义类加载器动态加载类(使用Class.forName(String name))的代码 我已经设置了自己的自定义ClassLoader,它应该可以配置为在尝试加载给定类时抛出ClassNotFoundException public class CustomTestClassLoader extends ClassLoader { private static String[] notAllowed = new String[]{};

最近,我一直在玩java中的类加载器,试图测试使用自定义
类加载器动态加载类(使用
Class.forName(String name)
)的代码

我已经设置了自己的自定义
ClassLoader
,它应该可以配置为在尝试加载给定类时抛出
ClassNotFoundException

public class CustomTestClassLoader extends ClassLoader {
    private static String[] notAllowed = new String[]{};
    public static void setNotAllowed(String... nonAllowedClassNames) {
        notAllowed = nonAllowedClassNames;
    }
    public static String[] getNotAllowed() {
        return notAllowed;
    }
    public CustomTestClassLoader(ClassLoader parent){super(parent);}
    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        for (String s : notAllowed) {
            if (name.equals(s)) {
                throw new ClassNotFoundException("Loading this class is not allowed for testing purposes.");
            }
        }

        if(name.startsWith("java") || name.startsWith("sun") || getClass().getName().equals(name)) {
            return getParent().loadClass(name);
        }

        Class<?> gotOne = super.findLoadedClass(name);
        if (gotOne != null) {
            return gotOne;
        }

        Class<?> c;
        InputStream in = getParent().getResourceAsStream(name.replace('.', '/')+".class");
        if (in == null) {
            throw new ClassNotFoundException("Couldn't locate the classfile: "+name);
        }
        try {
            byte[] classData = readBytes(in);
            c = defineClass(name, classData, 0, classData.length);
        } catch(IOException e) {
            throw new ClassNotFoundException("Couldn't read the class data.", e);
        } finally {
            try {
                in.close();
            } catch (IOException e) {/* not much we can do at this point */}
        }

        if (resolve) {
            resolveClass(c);
        }
        return c;
    }

    private byte[] readBytes(InputStream in) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buffer = new byte[4194304];
        int read = in.read(buffer);
        while (read != -1) {
            out.write(buffer, 0, read);
            read = in.read(buffer);
        }
        out.close();
        return out.toByteArray();
    }
}
现在我预计所有三个try块都会失败,因为
Class.forName
的文档说明它使用调用方的
ClassLoader
(在本测试中应该是custom/loader)。 但是,只有最后的try块失败。以下是我得到的输出:

notAllowed: com.classloadertest.test.Test
class com.classloadertest.test.Test
class com.classloadertest.test.Test
ClassLoader.loadClass failed
Class.forName
是否真的使用了
classloader
?如果是,有哪些方法? 它似乎在使用本机调用,所以我不知道它在幕后做什么

当然,如果有人知道测试
类.forName()
调用的任何替代方法,也会非常感激。

类.forName()
使用调用它的类的类加载器(例如,在您的例子中,包含
test()
方法的类)。因此,如果您在不同的环境中运行它,这将导致问题

更新该类加载器将用于加载您的
测试
类的
Class.forName()
。这可能就是解决方案:它可能是一个Eclipse定义的类加载器,可以访问您的类,因此它将加载它。尽管其父(或根)类装入器有明确的规则禁止装入该类


我仍然建议为这个实例化创建一个包装器类。您应该使用
CustomTestClassLoader
加载该类,然后您可以在该类中使用
class.forName()

对于openjdk,您可以在此处查看:您的类在添加到notAllowed之前没有加载?在@JEY之后,如果您设置NAME=MyClass.toString(),则您正在加载该类;内部类的行为也很奇怪。NAME确实设置为Test.class.getCanonicalName()。我认为调用Class.forName仍然会调用类加载器上的加载。如果没有,它可能使用本机查找,基本上绕过类加载器?我试着调试它,但是我无法单步执行原生的forName0方法。谢谢你的回答。我认为包含
test()
方法的类已经由我的自定义加载程序加载,因为我使用了
loader vmarg
。我用Eclipse调试器仔细检查了这个示例,以确保
Class.forName()
在这个示例中确实使用了
CustomTestClassLoader
的实例作为类加载器。我想我应该重新表述一下我所说的,所以现在开始:我验证了
Class.forName(NAME)
调用
Class.forName(NAME,true,loader)
其中
loader
是我的自定义类加载器的一个实例。因此,无需编写包装器,因为类加载器已正确解析。我建议执行以下步骤:(1)在调试模式下在Eclipe中运行测试,在
loadClass()
方法的开头设置断点(可以对类名设置条件,以避免大量中断)。(2) 如果没有帮助,请为您的类名设置一个类加载断点(在“运行”菜单下)。我刚刚做了一个新的测试,并注意到以下行为:如果
Class.forName
请求的类尚未加载,它将使用调用方的类加载程序的
loadClass
方法。但是,如果类已经被加载过一次,它将完全忽略类加载器,并且(这部分只是猜测)使用某种本机代码直接从JVM获取类。所以我现在的问题是,我有没有办法创建
类。forName
抛出
类NotFoundException
即使类已经加载了?你能从
类中调试你的代码吗。forName()
调用,它进入的第一个
类加载器
实例是什么?我也做了一些编辑,我不精确。
notAllowed: com.classloadertest.test.Test
class com.classloadertest.test.Test
class com.classloadertest.test.Test
ClassLoader.loadClass failed