简体   繁体   English

有没有办法动态创建类(而不是实例)?

[英]Is there a way to create class(not instance) dynamically?

I have an issue about how to create class(not instance) dynamically. 我有一个关于如何动态创建类(而不是实例)的问题。 In my project, I need to write several similar class according to the configuration file. 在我的项目中,我需要根据配置文件编写几个类似的类。 For exmaple, there is a JSON like this: 例如,有一个像这样的JSON:

{                                                                                                                                                                              
    {                                                                                                                                                                          
        "lang": "python",                                                                                                                                                      
        "file": "class1.py",
        "args": ["arg1"]                                                                                                                                  
    },                                                                                                                                                                         
    {                                                                                                                                                                          
        "lang": "python",                                                                                                                                                      
        "file": "class2.py"  
        "args": ["arg2"]                                                                                                                                               
    }                                                                                                                                                                          
} 

Subsequently, I need to write two java class below: 随后,我需要在下面编写两个java类:

class1: 1类:

public class Class1 extends ShellBolt implements IRichBolt {
    public Class1() {
        super("python", "class1.py");
    }

    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        declarer.declare(new Fields(arg1));
    }

    @Override
    public Map<String, Object> getComponentConfiguration() {
        return null;
    }
}

class2: 等级2:

public class Class2 extends ShellBolt implements IRichBolt {

    public Class2() {
        super("python", "class2.py");
    }

    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        declarer.declare(new Fields(arg2));
    }

    @Override
    public Map<String, Object> getComponentConfiguration() {
        return null;
    }
}

But if the JSON file is added a new object: 但是如果为JSON文件添加了一个新对象:

{                                                                                                                                                                          
    "lang": "python",                                                                                                                                                      
    "file": "class3.py"   
    "args": ["arg3"]
}

I need to write a new Class with the similar structure. 我需要编写一个具有类似结构的新类。 So, is there a way to create class dynamically? 那么,有没有办法动态创建类? I know maybe cglib might works, but I hava no idea how to use it in my case. 我知道也许cglib可能有效,但我不知道如何在我的情况下使用它。 Thanks. 谢谢。

You can use the JavaCompiler to compile any java code to a class file. 您可以使用JavaCompiler将任何Java代码编译为类文件。 You can then load the resulting class with a URLClassLoader . 然后,您可以使用URLClassLoader加载生成的类。

An example is given in the javadocs or for a complete example you can check this question . 在javadoc中给出了一个示例,或者您可以查看此问题的完整示例。

Applied to your Class1 it would look like the example below. 应用于您的Class1它看起来像下面的示例。 Note that you need to include all the relevant imports in the code or it won't compile. 请注意,您需要在代码中包含所有相关的导入,否则将无法编译。

public static void main(String[] args) throws Exception {
    String packageName = "some.packagename";
    String className = packageName + ".Class1";
    String body = "package " + packageName + ";   " +
                "public class Class1 extends ShellBolt implements IRichBolt {\n" +
                "    public Class1() {\n" +
                "        super(\"python\", \"class1.py\");\n" +
                "    }\n" +
                "\n" +
                "    @Override\n" +
                "    public void declareOutputFields(OutputFieldsDeclarer declarer) {\n" +
                "        declarer.declare(new Fields(arg1));\n" +
                "    }\n" +
                "\n" +
                "    @Override\n" +
                "    public Map<String, Object> getComponentConfiguration() {\n" +
                "        return null;\n" +
                "    }\n" +
                "}";

    Path classFile = Paths.get(System.getProperty("user.home"));
    compile(className, body, classFile);
    Class<?> class1 = loadClass(className, classFile);

    Method getComponentConfiguration = class1.getDeclaredMethod("getComponentConfiguration");
    Map<String, Object> result = (Map<String, Object>) getComponentConfiguration.invoke(class1.newInstance());
}

private static Class<?> loadClass(String className, Path path) throws Exception {
    URLClassLoader loader = new URLClassLoader(new URL[]{path.toUri().toURL()}, Test1.class.getClassLoader());
    return loader.loadClass(className);
}

private static void compile(String className, String body, Path path) throws Exception {
    List<JavaSourceFromString> sourceCode = Arrays.asList(new JavaSourceFromString(className, body));

    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
    fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(path.toFile()));
    boolean ok = compiler.getTask(null, fileManager, null, null, null, sourceCode).call();

    System.out.println("compilation ok = " + ok);
}

public static class JavaSourceFromString extends SimpleJavaFileObject {
    final String code;

    JavaSourceFromString(String name, String code) {
        super(URI.create("string:///" + name.replace('.', '/') + JavaFileObject.Kind.SOURCE.extension),
                JavaFileObject.Kind.SOURCE);
        this.code = code;
    }

    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) {
        return code;
    }
}

If you really need to create classes in JVM dynamically, consider using dynamic languages such as Groovy. 如果您确实需要动态地在JVM中创建类,请考虑使用Groovy等动态语言。

Another way: if you need to interpret some Python code on JVM - consider using Jython. 另一种方式:如果你需要在JVM上解释一些Python代码 - 考虑使用Jython。 http://www.jython.org/ http://www.jython.org/

There's a danger on creating classes in HotSpot JVM dynamically. 动态创建HotSpot JVM中的类存在危险。 If you will create too many dynamic classes, you may run out of PermGen space, and your JVM will crash with OutOfMemory error. 如果您将创建太多动态类,则可能会耗尽PermGen空间,并且您的JVM将因OutOfMemory错误而崩溃。 However, there's some workarounds about it, such as enabling garbage collection for PermGen via -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled jvm parameters. 但是,有一些相关的解决方法,例如通过-XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled为PermGen启用垃圾收集-XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled jvm参数。 It is announced than PermGen will be removed in JVM 8. 据宣布,PermGen将在JVM 8中删除。

Yes, there is a way. 是的,有一种方法。 You should create your own ClassLoader implementation, override its loadClass method and there call defineClass , providing it with the bytecode. 您应该创建自己的ClassLoader实现,覆盖其loadClass方法并调用defineClass ,为其提供字节码。 There is no built-in classes to generate bytecode, but you can always use ASM . 没有内置类来生成字节码,但您始终可以使用ASM Then you instantiate you ClassLoader and get your class from it that way: 然后你实例化ClassLoader并以这种方式从中获取你的类:

ClassLoader myClassLoader = new MyClassLoader(ClassLoader.getSystemClassLoader());
Class<?> generatedClass = Class.forName("pkg.GeneratedClass", true, myClassLoader);
Object instance = generatedClass.newInstance();

There is an example of how your loadClass might be implemented: 有一个如何实现loadClass的示例:

@Override public void loadClass(String name) {
    if (name.equals("pkg.GeneratedClass")) {
        ClassWriter cw = new ClassWriter(0);
        cw.visit(V1_6, ACC_PUBLIC, "pkg/GeneratedClass", null, "java/lang/Object", null);
        // and so on, use ASM API to complete class generation,
        // see asm documentation
        byte[] bytecode = cw.toByteArray();
        return defineClass(name, bytecode, 0, bytecode.length);
    } else {
        return super.loadClass(name);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM