Screen Shot 2018-04-06 at 14.09.00

Generate class during runtime with Javassist

Java + Javassist = Simple bytecode manipulation

Few days ago, I was trying to look for inspiration on internet about that how to generate java class during runtime and I was surprised by that I was not able to find any example. So here we go! It is really simple. You can get this code on git.

What we’ll need?

For easy bytecode manipulation I used Javassist, so we need this dependency from Maven Repository here.

I am using Gradle, so build.gradle looks like:

group 'org.hrabosch.examples'
version '1.0-SNAPSHOT'

apply plugin: 'java'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.12'

    compile group: 'org.javassist', name: 'javassist', version: '3.22.0-GA'
}

How I use it?

We have to get a default ClassPool, create a CtClass inside it, make CtMethod and add this method into generated class. Final step is convert CtClass into java.lang.Class.

Code:

public static Class generateClass(String className, String methodName, String methodBody)
      throws CannotCompileException {
    ClassPool pool = ClassPool.getDefault();
    CtClass cc = pool.makeClass(className);

    StringBuffer method = new StringBuffer();
    method.append("public void ")
          .append(methodName)
          .append("() {")
          .append(methodBody)
          .append(";}");

    cc.addMethod(CtMethod.make(method.toString(), cc));

    return cc.toClass();
  }

 Can I invoke this method?

Use reflection like this:

Class clazz = generateClass(className, methodName, methodBody);
Object obj = clazz.newInstance();
Method method = clazz.getDeclaredMethod(methodName);
method.invoke(obj);

That’s it!

Complete example code:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;

/**
 * Example main class with generating class during runtime and invoking generated method.
 *
 * @author hrabosch
 */
public class RuntimeClassGen {

  private static final String className = "MyClass";
  private static final String methodName = "printHello";
  private static final String methodBody = "java.lang.System.out.println(\"Hello world!\");";


  public static void main(String[] args) {
    try {
      // Use our static method to make a magic
      Class clazz = generateClass(className, methodName, methodBody);

      // Create a new instance of our newly generated class
      Object obj = clazz.newInstance();

      // Find our method in generated class
      Method method = clazz.getDeclaredMethod(methodName);

      // And finally invoke it on instance
      method.invoke(obj);

    } catch (IllegalAccessException e) {
      e.printStackTrace();
    } catch (InvocationTargetException e) {
      e.printStackTrace();
    } catch (InstantiationException e) {
      e.printStackTrace();
    } catch (NoSuchMethodException e) {
      e.printStackTrace();
    } catch (CannotCompileException e) {
      e.printStackTrace();
    }
  }

  public static Class generateClass(String className, String methodName, String methodBody)
      throws CannotCompileException {
    ClassPool pool = ClassPool.getDefault();
    CtClass cc = pool.makeClass(className);

    StringBuffer method = new StringBuffer();
    method.append("public void ")
          .append(methodName)
          .append("() {")
          .append(methodBody)
          .append(";}");

    cc.addMethod(CtMethod.make(method.toString(), cc));

    return cc.toClass();
  }

}

In this simple example, I am using only simple method without parameters, but there is no problem to use parameters inside generated method and invoke method via reflection with them.

Note that we have to use fully-qualified name of class, because classes are not imported for generated class, in this example it was: java.lang.System.

Aleš Laňar
Senior Engineer Software ve společnosti CA Technologies

Leave a Reply

Your email address will not be published. Required fields are marked *