3

我在一个servlet加载一个文件,使用.getClassLoader().getResourceAsStream(path),路径在WEB-INF/classes目录,我改变路径文件内容后发现,但是文件servlet加载是一样的,不要改变, 文件被缓存。

示例代码:

在我更改 test.key 内容后,此方法每次总是得到相同的结果

private String getKey(String param){
    String name = "keys/"+param+"/test.key";
    InputStream in = XXXServlet.class.getClassLoader().getResourceAsStream(name);
    StringBuilder builder = new StringBuilder();
    try {
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        String line = null;
        while((line = reader.readLine()) != null){
            builder.append(line).append("\n");
        }
    } catch (IOException ignoreException) {

    }finally{
        try {
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    String result = builder.toString();
    return result;
}

==================================================== ================

更改这两行代码,它工作正常

    String name = "/WEB-INF/classes/keys/"+param+"/test.key";
    InputStream in = getServletContext().getResourceAsStream(name);
4

2 回答 2

0

安库尔是对的。如果名称和相关内容已经被类加载器或其父级加载过一次,则试图获取类或资源(无论文件/输入流的名称和路径如何)的现有类加载器将永远不会重新加载。这是出于性能原因。唯一的方法是创建一个类加载器的新实例,然后再做一次。但是,至少对于类,您将不得不担心不兼容的类在系统中一起运行。就像您不能将新类的实例分配给使用第一个类加载器实例加载的类键入的变量一样,因为它们在技术上是不同的类。

Pabrantes 认为这是不同的,因为 Liu 不是按说加载一个“类”,而是一个键:“keys/”+param+“/test.key”;然而,他正在使用类加载器来执行此操作,并且在加载“名称”方面的规则与 getResourceAsStream(name) 相同。不管它是否是一个类,类加载器都会认为“哦,给你,我已经为'name'加载了字节流”。把它从 permgen 中拉出来。对于那些感兴趣的人——如果你创建/实现你自己版本的每次重新加载的新类加载器——只需确保它只对非常特定的路径或名称模式执行此操作。另外请记住,您加载的每个副本都可能会在 permgen 中获得空间,因此随着时间的推移 permgen 将失去控制,除非您卸载。

所以 - 这就是为什么它不起作用。ContextLoader 很好用。:-)

丹 C.

于 2014-12-06T00:17:00.007 回答
-1

这是因为一旦你加载了一个类,即使你尝试重新加载它,你也会得到相同的类内容。这是因为它检查类内容是否已经加载,如果是,那么它不会费心再次加载它,只是引用已经加载的内容(在内存中)。

要实现重新加载,您需要编写一个自定义类加载器,它将读取类的内容(以字节为单位)并使用这些字节来加载类。

示例 CustomClassLoader 代码:

public class CustomClassLoader extends ClassLoader {

private static final String CLASS_FOLDER_PATH = "/lib/all-classfiles/";
private static final String CLASS_FILE_EXTENSION = ".class";


/**
 * Loads the class file in memory
 */
@Override
public Class<?> loadClass(String className) throws ClassNotFoundException {
    return findClass(className);
}

@Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
    try {

        byte[] bytes = loadClassData(className);

        // Build the class based on the byte data
        return defineClass(className, bytes, 0, bytes.length);

    } catch (IOException ioException) {
        // Call super class(ClassLoader) implementation of loadClass() method
        return super.loadClass(className);
    }
}

/**
 * Returns the byte contents of the class file
 * 
 * @param className
 * @return byte[] - class file data
 * @throws IOException
 */
private byte[] loadClassData(String className) throws IOException {

    File classFile = new File(CLASS_FOLDER_PATH + className + CLASS_FILE_EXTENSION);

    int fileSize = (int) classFile.length();

    // Read the file bytes in byte array
    byte buff[] = new byte[fileSize];

    FileInputStream fis = null;
    DataInputStream dis = null;

    fis = new FileInputStream(classFile);
    dis = new DataInputStream(fis);

    // Read the byte contents of the ORM class file for loading the class
    dis.readFully(buff);

    dis.close();
    fis.close();

    return buff;
}

}

当您想要重新加载类的内容时,请确保您的类加载器实例已被垃圾回收。否则,您最终可能会读取缓存的类数据。

在重新加载时,您还需要处理更多的事情。请阅读这篇关于Class Reloading的文章以详细了解它。

希望这可以帮助。

于 2013-03-13T09:23:40.003 回答