2014-01-17 149 views
5

我有一個關於如何動態創建類(不是實例)的問題。 在我的項目中,我需要根據配置文件編寫幾個類似的類。 對於〔實施例,有這樣的JSON:有沒有辦法動態創建類(不是實例)?

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

隨後,我需要以下兩個Java類寫:

的Class1:

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; 
    } 
} 

等級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; 
    } 
} 

但是,如果JSON文件添加了新對象:

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

我需要編寫一個具有類似結構的新類。 那麼,有沒有辦法動態創建類? 我知道也許cglib可能有效,但我不知道如何在我的情況下使用它。 謝謝。

+2

爲什麼你需要創建這些類?爲什麼僅僅只有一個類從配置文件中封裝JSON對象並具有根據該對象的內容行爲的方法是不夠的? – Philipp

+0

有像[BCEL](http://commons.apache.org/proper/commons-bcel/)這樣的庫,允許您在運行時創建類 –

+0

這是一篇討論類似問題的文章:http:// www .javaranch.com/journal/200711/Journal200711.jsp#a4 – jayeff

回答

2

您可以使用JavaCompiler將任何java代碼編譯爲類文件。然後,您可以使用URLClassLoader加載生成的類。

在javadoc中給出了一個例子,或者您可以查看一個完整的例子this question

適用於您的Class1它看起來像下面的例子。請注意,您需要在代碼中包含所有相關的導入,否則將無法編譯。

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; 
    } 
} 
1

如果你真的需要動態創建在JVM中的類,可以考慮使用動態語言如Groovy的。

另一種方法:如果您需要解釋JVM上的一些Python代碼 - 請考慮使用Jython。 http://www.jython.org/

動態地在HotSpot JVM中創建類存在危險。如果您將創建太多動態類,則可能會耗盡PermGen空間,並且您的JVM將以OutOfMemory錯誤的方式崩潰。但是,有一些解決方法,例如通過-XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled jvm參數爲PermGen啓用垃圾收集。這是宣佈比PermGen將在JVM 8中刪除。

0

是的,有一種方法。您應該創建自己的ClassLoader實現,覆蓋其loadClass方法,並在那裏調用defineClass,爲其提供字節碼。沒有內置的類來生成字節碼,但您始終可以使用ASM。然後,你實例化你ClassLoader,並從它那裏得到你的類,它的方法:

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

有一個例子,說明你的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); 
    } 
} 
相關問題