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

标签 java unit-testing classloader

我最近一直在研究 Java 中的 ClassLoader,试图测试使用自定义 ClassLoader< 动态加载类(使用 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();
    }
}

我正在使用 -Djava.system.class.loader=com.classloadertest.test.CustomTestClassLoader 将此 classloader 设置为默认 ClassLoader。 我希望能够通过使用 CustomTestClassLoader.setNotAllowed(String...) 禁止某些类名来强制执行 ClassNotFoundException。 但是,它仅适用于 ClassLoader.loadClass,不适用于 Class.forName:

public void test() {
    ClassLoader loader = this.getClass().getClassLoader();
    CustomTestClassLoader custom = (CustomTestClassLoader)loader; 
    CustomTestClassLoader.setNotAllowed(NAME);
    for (String s : custom.getNotAllowed())
        System.out.println("notAllowed: "+s);
    try {
        System.out.println(Class.forName(NAME));
    } catch (ClassNotFoundException e) {
        System.out.println("forName(String) failed");
    }
    try {
        System.out.println(Class.forName(NAME,false,custom));
    } catch (ClassNotFoundException e) {
        System.out.println("forName(String,boolean,ClassLoader) failed");
    }
    try {
        System.out.println(custom.loadClass(NAME));
    } catch (ClassNotFoundException e) {
        System.out.println("ClassLoader.loadClass failed");
    }
}

现在我预计所有三个 try block 都会失败,因为 Class.forName 的文档说它使用调用者的 ClassLoader(应该是 custom/loader in这个测试)。 但是,只有最后一个 try block 失败。这是我得到的输出:

notAllowed: com.classloadertest.test.Test
class com.classloadertest.test.Test
class com.classloadertest.test.Test
ClassLoader.loadClass failed

Class.forName 真的使用了 classloader 吗?如果是这样,哪些方法? 它似乎在使用 native 调用,所以我不知道它在幕后做了什么。

当然,如果有人知道测试 Class.forName() 调用的任何替代方法,我们也将不胜感激。

最佳答案

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

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

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

关于java - 如何在 Java 代码中测试 Class.forName 调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14354567/

相关文章:

java - Spring Boot 2 测试json序列化

c# - 为提供给 xUnit 理论的数据集项目分配一个友好名称,用于在理论名称后显示在结果中

java - 仅更改方法结果和类加载器 NoSuchMethod

java - 查询在 Mongo 中工作,但不适用于 spring-data-mongo

java - 在 Java 中链接方法调用时遇到问题

java - 充分利用 Java 中的多态性

unit-testing - 使用 grpc.SetHeader 或 grpc.SendHeader 调用测试函数时无法设置 header

java - 在java中强制在程序启动时加载类

java - 如何调试 JVM 资源加载?

java - tfs 中的结账问题