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:
{
{
"lang": "python",
"file": "class1.py",
"args": ["arg1"]
},
{
"lang": "python",
"file": "class2.py"
"args": ["arg2"]
}
}
Subsequently, I need to write two java class below:
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;
}
}
class2:
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:
{
"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. Thanks.
You can use the JavaCompiler to compile any java code to a class file. You can then load the resulting class with a URLClassLoader.
An example is given in the javadocs or for a complete example you can check this question.
Applied to your Class1
it would look like the example below. 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 love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With