Java forName(名称、实例化、类加载器)不';不要将类添加到类路径

Java forName(名称、实例化、类加载器)不';不要将类添加到类路径,java,classloader,dynamic-class-loaders,Java,Classloader,Dynamic Class Loaders,我在运行时生成.java类文件,需要立即在代码中使用这些类。 因此,我使用编译器API编译.java类以生成.class文件: JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>(); StandardJavaFileManager manager

我在运行时生成
.java
类文件,需要立即在代码中使用这些类。 因此,我使用编译器API编译
.java
类以生成
.class
文件:

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();

StandardJavaFileManager manager = compiler.getStandardFileManager(diagnostics, null, null);

File file = new File("path to file");

Iterable<? extends JavaFileObject> sources = manager.getJavaFileObjectsFromFiles(Arrays.asList(file));

CompilationTask task = compiler.getTask(null, manager, diagnostics, null, null, sources);

task.call();

manager.close();
另一种方法是使用
Class.forName(name,instantiation,classLoader)
将类添加到类路径(同时提供类引用)。第一个方法我无法应用,因为我遇到了上面提到的编译器错误(Java11)。 关于第二种方法,如果我们像这样调用默认类加载器,
Class.forName(名称、实例化、类加载器)
是否将新类附加到
classpath

Class.forName("com.foo.Bar",true, ClassLoader.getSystemClassLoader());
// or:
Class.forName("com.foo.Bar",true, ApiHandler.class.getClassLoader());
这对我不起作用。上述类加载器参数的哪种变体 是正确的,为什么这些不起作用?是否必须创建自定义类加载器并将其传递给
Class.forName()

3.我正在eclipse项目的
src
文件夹中的
com.foo
包中制作
.java
文件。它们编译的
.class
文件也在同一文件夹中生成(使用编译器API)。当我使用eclipse刷新项目时(右键单击项目->刷新),相关的
.class
文件将在
target/classes
文件夹中生成,此时可以通过代码访问类(例如使用
class.forName(“com.foo.Bar”)
。如果我生成
.class
文件,可能是这样吗(通过编译器API)在
target/classes
文件夹中,不需要将类引入类路径,就可以识别这些类


更新:

通过将受尊重的
.class
文件保存在项目的
target/classes
文件夹中(在上面的第三个问题中提到),我可以在代码中使用编译的类。(通过向编译器的
getTask()
方法添加
-d
选项:

    // Get the ClassLoader class
    ClassLoader cl = ClassLoader.getSystemClassLoader();
    Class<?> clazz = cl.getClass();

    // Get the protected addURL method from the parent URLClassLoader class
    Method method = clazz.getSuperclass().getDeclaredMethod("addURL", new Class[] { URL.class });

    // Run projected addURL method to add JAR to classpath
    method.setAccessible(true);
    method.invoke(cl, new Object[] { cls });
Iterable<String> options = Arrays.asList( new String[] { "-d", System.getProperty("user.dir") + "/target/classes/"} );
.
.
.

CompilationTask task = compiler.getTask(null, manager, diagnostics, options, null, sources);
当然,还有通过类加载器的方式:

ClassLoader classLoader = ClassLoader.getSystemClassLoader(); 

Class<?> cls = classLoader.loadClass("com.foo.Bar");
ClassLoader ClassLoader=ClassLoader.getSystemClassLoader();
Class cls=classLoader.loadClass(“com.foo.Bar”);

最安全的解决方案是创建一个新的
类加载器
实现,并通过新加载器加载生成的类,如中所示

但自Java 9以来,如果尚未定义/加载具有该名称的类,则有可能在您自己的上下文中定义类,即在同一个包中定义类。这样的类定义甚至可以取代类路径上的定义,如前所述,只要它尚未加载。因此,不仅仅是后续的
class.forName(String)
调用将解析到此类定义,但即使是非反射引用

这可以通过以下程序进行演示

class Dummy { // to make the compiler happy
    static native void extensionMethod();
}
public class CompileExtension {
    public static void main(String[] args) throws IOException, IllegalAccessException {
        // customize these, if you want, null triggers default behavior
        DiagnosticListener<JavaFileObject> diagnosticListener = null;
        Locale locale = null;

        // the actual class implementation, to be present at runtime only
        String class1 =
            "class Dummy {\n"
          + "    static void extensionMethod() {\n"
          + "        System.out.println(\"hello from dynamically compiled code\");\n"
          + "    }\n"
          + "}";
        JavaCompiler c = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fm
          = c.getStandardFileManager(diagnosticListener, locale, Charset.defaultCharset());
        // define where to store compiled class files - use a temporary directory
        fm.setLocation(StandardLocation.CLASS_OUTPUT,
            Set.of(Files.createTempDirectory("compile-test").toFile()));
        JavaCompiler.CompilationTask task = c.getTask(null, fm,
            diagnosticListener, Set.of(), Set.of(),
            Set.of(new SimpleJavaFileObject(
                URI.create("string:///Class1.java"), JavaFileObject.Kind.SOURCE) {
                    public CharSequence getCharContent(boolean ignoreEncodingErrors) {
                        return class1;
                    }
                }));

        if(task.call()) {
            FileObject fo = fm.getJavaFileForInput(
                StandardLocation.CLASS_OUTPUT, "Dummy", JavaFileObject.Kind.CLASS);
            // these are the class bytes of the first class
            byte[] classBytes = Files.readAllBytes(Paths.get(fo.toUri()));
            MethodHandles.lookup().defineClass(classBytes);

            Dummy.extensionMethod();
        }
    }
}
class Dummy{//使编译器满意
静态本机void extensionMethod();
}
公共类编译器扩展{
publicstaticvoidmain(字符串[]args)抛出IOException、IllegalAccessException{
//如果需要,可以自定义这些null触发器的默认行为
DiagnosticListener DiagnosticListener=null;
Locale=null;
//实际的类实现,仅在运行时出现
字符串类1=
“类虚拟{\n”
+“静态void extensionMethod(){\n”
+“System.out.println(\“动态编译代码的hello\”);\n”
+“}\n”
+ "}";
JavaCompiler c=ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fm
=c.getStandardFileManager(diagnosticListener,locale,Charset.defaultCharset());
//定义编译类文件的存储位置-使用临时目录
fm.设置位置(StandardLocation.CLASS_输出,
Set.of(Files.createTempDirectory(“编译测试”).toFile());
JavaCompiler.CompilationTask task=c.getTask(null,fm,
diagnosticListener,Set.of(),Set.of(),
集合(新的SimpleJavaFileObject)(
URI.create(“string:///Class1.java“”,JavaFileObject.Kind.SOURCE){
public CharSequence getCharContent(布尔ignoreEncodingErrors){
返回类别1;
}
}));
if(task.call()){
FileObject fo=fm.getJavaFileForInput(
StandardLocation.CLASS_输出,“Dummy”,JavaFileObject.Kind.CLASS);
//这些是第一个类的类字节
byte[]classBytes=Files.readAllBytes(path.get(fo.toUri());
MethodHandles.lookup().defineClass(类字节);
Dummy.extensionMethod();
}
}
}
Dummy
定义的存在只是为了能够在编译时插入对所需方法的调用,而在运行时,动态定义的类在调用该方法之前取代它


但是要小心处理。如上所述,自定义类加载器是最安全的解决方案。通常,您应该通过始终存在且仅动态加载实现的接口创建对扩展的编译时引用,该接口可以在运行时强制转换到接口,然后通过接口定义的API使用。

URIClassLoad呃?@dan1st如果你指的是制作自定义类加载器的代码片段,我从中获得了代码,这里还有一个使用
URLClassLoader
制作自定义类加载器的表示:我认为它可以帮助你。@dan1st谢谢,我不知道如何使用它,与我之前评论中的上面链接不同。你的代码是“one is using a custom ClassLoader”后面的IPET没有使用自定义类加载器,而是试图侵入系统类加载器。使用自定义类加载器是@dan1st建议的,创建一个新的
URLClassLoader
class Dummy { // to make the compiler happy
    static native void extensionMethod();
}
public class CompileExtension {
    public static void main(String[] args) throws IOException, IllegalAccessException {
        // customize these, if you want, null triggers default behavior
        DiagnosticListener<JavaFileObject> diagnosticListener = null;
        Locale locale = null;

        // the actual class implementation, to be present at runtime only
        String class1 =
            "class Dummy {\n"
          + "    static void extensionMethod() {\n"
          + "        System.out.println(\"hello from dynamically compiled code\");\n"
          + "    }\n"
          + "}";
        JavaCompiler c = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fm
          = c.getStandardFileManager(diagnosticListener, locale, Charset.defaultCharset());
        // define where to store compiled class files - use a temporary directory
        fm.setLocation(StandardLocation.CLASS_OUTPUT,
            Set.of(Files.createTempDirectory("compile-test").toFile()));
        JavaCompiler.CompilationTask task = c.getTask(null, fm,
            diagnosticListener, Set.of(), Set.of(),
            Set.of(new SimpleJavaFileObject(
                URI.create("string:///Class1.java"), JavaFileObject.Kind.SOURCE) {
                    public CharSequence getCharContent(boolean ignoreEncodingErrors) {
                        return class1;
                    }
                }));

        if(task.call()) {
            FileObject fo = fm.getJavaFileForInput(
                StandardLocation.CLASS_OUTPUT, "Dummy", JavaFileObject.Kind.CLASS);
            // these are the class bytes of the first class
            byte[] classBytes = Files.readAllBytes(Paths.get(fo.toUri()));
            MethodHandles.lookup().defineClass(classBytes);

            Dummy.extensionMethod();
        }
    }
}