动态编译依赖于特定类加载器加载的类的java代码

动态编译依赖于特定类加载器加载的类的java代码,java,groovy,compilation,jvm,classloader,Java,Groovy,Compilation,Jvm,Classloader,我们能够动态编译java代码。 我至少知道而且 但他们似乎无法编译依赖于某个类加载器中的某个类的类 是否可以动态编译依赖于仅在特定类加载器中可用的类的java代码?比如说: ClassLoader classloader = ... // only this CL can load class 'com.External' String source = "public class MyClass extends com.External {}"; Class<?> compiled

我们能够动态编译java代码。 我至少知道而且

但他们似乎无法编译依赖于某个类加载器中的某个类的类

是否可以动态编译依赖于仅在特定类加载器中可用的类的java代码?比如说:

ClassLoader classloader = ... // only this CL can load class 'com.External'
String source = "public class MyClass extends com.External {}";
Class<?> compiled = DesiredDynamicCompiler.compile("MyClass", source, classloader); 
// last argument is like an information to compiler where to search all dependencies
ClassLoader ClassLoader=…//只有此CL才能加载类“com.External”
String source=“公共类MyClass扩展com.External{}”;
Class compiled=DesiredDynamicCompiler.compile(“MyClass”,源代码,类加载器);
//最后一个参数类似于编译器搜索所有依赖项的信息

为了提供更多的见解:我想在java中做GroovyClassLoader在groovy中可以做的事情:

GroovyClassLoader groovyClassLoader = new GroovyClassLoader(classLoader);
Class<?> parsedClass = groovyClassLoader.parseClass("some source");
GroovyClassLoader-GroovyClassLoader=newgroovyclassloader(classLoader);
类parsedClass=groovyClassLoader.parseClass(“某个源”);

该代码可以解析依赖于仅在指定类加载器中可用的类的类。

您的类路径上应该有所有依赖项。您提到的工具在封面下使用Java编译器API

它不与当前JVM内存中的类交互,它只搜索类路径中的依赖项

您可以继续->->进一步了解那里发生的情况


您可以尝试做的一件事是将动态编译的依赖项作为.class文件转储到类路径中的适当位置,以便编译过程将其提取。

除非能够提供其定义类的类字节,否则无法使用
类加载器作为引用。也就是说,如果您有一个表示顶级类的
Class
实例,则可以使用
classInstance.getResourceAsStream(classInstance.getSimpleName()+“.Class”)
尝试获得类字节。如果您可以访问组成动态类的字节,那么可以通过实现将它们提供给java编译器

编译器API是标准API的一部分,不需要第三方库。下面的代码通过先编译测试类,然后根据上一步中刚刚创建的类设置编译第二个类所需的环境来演示这一点:

// customize these, if you want, null triggers default behavior
DiagnosticListener<JavaFileObject> diagnosticListener = null;
Locale locale = null;

// the first class, to be present at runtime only
String class1 = "package test;\npublic class Class1 {}";
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, Collections.singleton(
        Files.createTempDirectory("compile-test").toFile()));
JavaCompiler.CompilationTask task = c.getTask(null, fm,
    diagnosticListener, Collections.emptySet(), Collections.emptySet(),
    Collections.singleton(new SimpleJavaFileObject(
        URI.create("string:///Class1.java"), Kind.SOURCE) {
            public CharSequence getCharContent(boolean ignoreEncodingErrors) {
                return class1;
            }
        }));
if(task.call()) {
    FileObject fo = fm.getJavaFileForInput(
            StandardLocation.CLASS_OUTPUT, "test.Class1", Kind.CLASS);
    // these are the class bytes of the first class
    byte[] class1bytes = Files.readAllBytes(Paths.get(fo.toUri()));

    // the actual task: define a class dependent on the first class
    String class2 = "package test;\npublic class Class2 { Class1 variable; }";

    // create a file object representing the dynamic class
    JavaFileObject jo = new SimpleJavaFileObject(
        URI.create("runtime:///test/Class1.class"), Kind.CLASS) {
            @Override public InputStream openInputStream() throws IOException {
                return new ByteArrayInputStream(class1bytes);
            }
        };

    // and a custom file manager knowing how to locate that class
    JavaFileManager myFM = new ForwardingJavaFileManager(fm) {
        @Override
        public JavaFileObject getJavaFileForInput(
                JavaFileManager.Location location, String className, Kind kind)
                throws IOException {
            if(location==StandardLocation.CLASS_PATH&&className.equals("test.Class1")) {
                return jo;
            }
            return super.getJavaFileForInput(location, className, kind);
        }

        @Override
        public boolean hasLocation(JavaFileManager.Location location) {
            return location==StandardLocation.CLASS_PATH || super.hasLocation(location);
        }

        @Override
        public Iterable list(JavaFileManager.Location location,
                String packageName, Set kinds, boolean recurse) throws IOException {
            if(location==StandardLocation.CLASS_PATH
                    && (packageName.equals("test") || recurse&&packageName.isEmpty())) {
                return Collections.singleton(jo);
            }
            return super.list(location, packageName, kinds, recurse);
        }

        @Override
        public String inferBinaryName(
                JavaFileManager.Location location, JavaFileObject file) {
            if(file==jo) return "test.Class1";
            return super.inferBinaryName(location, file);
        }
    };
    // compile the second class using the custom file manager to locate dependencies
    task = c.getTask(null, myFM,
        diagnosticListener, Collections.emptySet(), Collections.emptySet(),
        Collections.singleton(new SimpleJavaFileObject(
            URI.create("string:///Class2.java"), Kind.SOURCE) {
                public CharSequence getCharContent(boolean ignoreEncodingErrors) {
                    return class2;
                }
            }));
    if(task.call()) {
        fo = fm.getJavaFileForInput(
            StandardLocation.CLASS_OUTPUT, "test.Class2", Kind.CLASS);
        // there we have the compiled second class
        byte[] class2bytes = Files.readAllBytes(Paths.get(fo.toUri()));
    }
}
//如果需要,可以自定义这些null触发器默认行为
DiagnosticListener DiagnosticListener=null;
Locale=null;
//第一个类,仅在运行时出现
String class1=“包测试;\n公共类class1{}”;
JavaCompiler c=ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fm
=c.getStandardFileManager(diagnosticListener,locale,Charset.defaultCharset());
//定义编译类文件的存储位置-使用临时目录
fm.setLocation(StandardLocation.CLASS_输出,Collections.singleton(
createTempDirectory(“编译测试”).toFile();
JavaCompiler.CompilationTask task=c.getTask(null,fm,
diagnosticListener,Collections.emptySet(),Collections.emptySet(),
Collections.singleton(新的SimpleJavaFileObject(
URI.create(“string:///Class1.java(来源:Kind.SOURCE){
public CharSequence getCharContent(布尔ignoreEncodingErrors){
返回类别1;
}
}));
if(task.call()){
FileObject fo=fm.getJavaFileForInput(
标准位置.CLASS_输出,“test.Class1”,种类.CLASS);
//这些是第一个类的类字节
byte[]class1bytes=Files.readAllBytes(path.get(fo.toUri());
//实际任务:定义一个依赖于第一个类的类
String class2=“包测试;\n公共类class2{Class1变量;}”;
//创建表示动态类的文件对象
JavaFileObject jo=新的SimpleJavaFileObject(
URI.create(“runtime:///test/Class1.class(一类,一类){
@重写公共InputStream openInputStream()引发IOException{
返回新的ByteArrayInputStream(ClassBytes);
}
};
//还有一个自定义文件管理器,知道如何定位该类
JavaFileManager myFM=新转发JavaFileManager(fm){
@凌驾
公共JavaFileObject getJavaFileForInput(
JavaFileManager.Location-Location,String-className,Kind)
抛出IOException{
if(location==StandardLocation.CLASS_PATH&&className.equals(“test.Class1”)){
返回jo;
}
返回super.getJavaFileForInput(位置、类名、种类);
}
@凌驾
公共布尔hasLocation(JavaFileManager.Location){
返回位置==StandardLocation.CLASS|u PATH | super.hasLocation(位置);
}
@凌驾
公共可编辑列表(JavaFileManager.Location,
字符串packageName、集合种类、布尔递归)引发IOException{
if(location==StandardLocation.CLASS_路径
&&(packageName.equals(“test”)| | recurse&&packageName.isEmpty()){
返回集合。单例(jo);
}
返回超级列表(位置、packageName、种类、递归);
}
@凌驾
公共字符串推断二进制名称(
JavaFileManager.Location位置,JavaFileObject文件){
if(file==jo)返回“test.Class1”;
返回super.inferBinaryName(位置、文件);
}
};
//使用自定义文件管理器编译第二个类以查找依赖项
task=c.getTask(null,myFM,
diagnosticListener,Collections.emptySet(),Collections.emptySet(),
Collections.singleton(新的SimpleJavaFileObject(
URI.create(“string:///Class2.java(来源:Kind.SOURCE){
public CharSequence getCharContent(布尔ignoreEncodingErrors){
R