我有一个关于如何动态创建类(而不是实例)的问题。 在我的项目中,我需要根据配置文件编写几个类似的类。 例如,有一个像这样的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可能有效,但我不知道如何在我的情况下使用它。 感谢。
答案 0 :(得分:2)
您可以使用JavaCompiler将任何Java代码编译为类文件。然后,您可以使用URLClassLoader加载生成的类。
javadocs中给出了一个示例,或者您可以查看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 :(得分: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中删除。
答案 2 :(得分: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);
}
}