动态重新编译并重新加载类 [英] Dynamically recompile and reload a class

查看:114
本文介绍了动态重新编译并重新加载类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用Java构建一个可以接收Java源文件的服务器,它应该使用JavaCompiler动态地对其进行编译,然后加载该类.但是,问题在于,如果服务器接收到具有相同名称但内容不同的文件,它将仍然加载先前的类并提供相同的输出.我已经注意到一些答案,建议为要尝试加载的类创建一个超类并使用其他classLoader,但是如果将Java源文件动态发送到服务器,情况仍然如此吗?

这是我在FileServer.java中的编译和加载方法:

public final static int FILE_SIZE = 1022386;

public static void compile(String fileName)
{
// Save source in .java file.
    File sourceFile = new File(fileName);

    // Compile source file.
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

    DiagnosticCollector <JavaFileObject> diagnostics =
        new DiagnosticCollector<JavaFileObject>();
    StandardJavaFileManager fileManager = 
        compiler.getStandardFileManager(diagnostics, null, null);  
    File [] files = new File [] {sourceFile};
    Iterable<? extends JavaFileObject> compilationUnits =
        fileManager.getJavaFileObjectsFromFiles(Arrays.asList(files));

    String [] compileOptions = new String[] {"-classpath", "runtime.jar"};
    Iterable<String> compilationOptions = Arrays.asList(compileOptions);

    JavaCompiler.CompilationTask task =
        compiler.getTask(null, fileManager, diagnostics, compilationOptions, 
                null, compilationUnits);
    task.call();

}

public static void compileLoad (String fileName)
{
compile(fileName);

    String className = "";
    int i = 0;
    while(fileName.charAt(i) != '.') {
        className += fileName.charAt(i);
        i++;
    }

ClassLoader classLoader = FileServer.class.getClassLoader();
    // Dynamically load class and invoke its main method.
    try {
        //Class<?> cls = Class.forName(className);
    Class<?> cls = classLoader.loadClass(className);
        Method meth = cls.getMethod("main", String[].class);
        String[] params = null;
        meth.invoke(null, (Object) params);
    } catch (Exception e) {
        e.printStackTrace();     
    }
}

解决方案

问题是ClassLoader.loadClassClass.forName的正常行为是返回现有的Class(如果先前已加载).他们不会查看类文件是否已更改.

(这是有充分的理由的.基本上,Java中对象类型的标识等效于一个由完全限定名称的类及其类加载器组成的元组.这意味着JVM无法(并且不会) )允许类加载器定义"两个具有相同名称的类.如果您尝试这样做,我认为defineClass方法将为您返回先前加载的类的Class对象.)

所以...

为了实现您要实现的目标,您需要每次 创建一个新的ClassLoader,以加载类的新版本.

I'm building a server in java that can receive java source files, and it should dynamically compile it using JavaCompiler and then load the class. However the problem is that if the server receive a file with the same name but different content, it will still load the previous class and gave the same outputs. I've noticed some answers suggesting creating a superclass for the class I'm trying to load and using a different classLoader, but is it still the case if the java source file is dynamically sent to the server?

Here's my compile and load methods in FileServer.java:

public final static int FILE_SIZE = 1022386;

public static void compile(String fileName)
{
// Save source in .java file.
    File sourceFile = new File(fileName);

    // Compile source file.
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

    DiagnosticCollector <JavaFileObject> diagnostics =
        new DiagnosticCollector<JavaFileObject>();
    StandardJavaFileManager fileManager = 
        compiler.getStandardFileManager(diagnostics, null, null);  
    File [] files = new File [] {sourceFile};
    Iterable<? extends JavaFileObject> compilationUnits =
        fileManager.getJavaFileObjectsFromFiles(Arrays.asList(files));

    String [] compileOptions = new String[] {"-classpath", "runtime.jar"};
    Iterable<String> compilationOptions = Arrays.asList(compileOptions);

    JavaCompiler.CompilationTask task =
        compiler.getTask(null, fileManager, diagnostics, compilationOptions, 
                null, compilationUnits);
    task.call();

}

public static void compileLoad (String fileName)
{
compile(fileName);

    String className = "";
    int i = 0;
    while(fileName.charAt(i) != '.') {
        className += fileName.charAt(i);
        i++;
    }

ClassLoader classLoader = FileServer.class.getClassLoader();
    // Dynamically load class and invoke its main method.
    try {
        //Class<?> cls = Class.forName(className);
    Class<?> cls = classLoader.loadClass(className);
        Method meth = cls.getMethod("main", String[].class);
        String[] params = null;
        meth.invoke(null, (Object) params);
    } catch (Exception e) {
        e.printStackTrace();     
    }
}

解决方案

The problem is that the normal behaviour for ClassLoader.loadClass or Class.forName is to return an existing Class if it was loaded previously. They won't look at the class file to see if it has changed.

(There is a good reason for this. Basically, the identity of an object type in Java is equivalent to a tuple consisting of the classes fully qualified name, and its class loader. This means that the JVM cannot (and will not) allow a classloader to "define" two classes with the same name. If you try to make this happen, I think that the defineClass method will just give you back the Class object for the previously loaded class.)

So ...

In order to achieve what you are trying to achieve, you need to create a new ClassLoader each time you want to load a new version of a class.

这篇关于动态重新编译并重新加载类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆