Posts tagged: bytecode
Javassist is a really easy to use bytecode manipulation library.
Creating class proxies with Javassist is straight forward, just create an instance of ProxyFactory, a corresponding MethodHandler, that handles the method invocation and the class proxy itself.
Find below a small proxy creation example, that traces the execution of all class methods:
public class ProxyFactoryExample { public void foo() { System.out.println("Foo method executed."); } public void bar() { try { Thread.sleep(500); } catch (InterruptedException e) { // ignore } System.out.println("Bar method executed."); } public static void main(String[] args) throws Throwable { ProxyFactory proxy = new ProxyFactory(); proxy.setSuperclass(ProxyFactoryExample.class); proxy.setInterfaces(new Class[] { Serializable.class }); proxy.setFilter(new MethodFilter() { @Override public boolean isHandled(Method m) { // skip finalize methods return !(m.getParameterTypes().length == 0 && m.getName() .equals("finalize")); } }); MethodHandler tracingMethodHandler = new MethodHandler() { @Override public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable { long start = System.currentTimeMillis(); try { return proceed.invoke(self, args); } finally { long end = System.currentTimeMillis(); System.out.println("Execution time: " + (end - start) + " ms, method: " + proceed); } } }; ProxyFactoryExample obj = (ProxyFactoryExample) proxy.create( new Class[0], new Object[0], tracingMethodHandler); obj.foo(); obj.bar(); } }
Imagine your application have to create Java POJO instances dynamically at runtime from some external configuration. This task can be easily done using one of the bytecode manipualtion library. This post demonstrates how this can be done using Javassist library.
Assume we have following configuration for the properties our dynamically created POJO should contain:
Map<String, Class<?>> props = new HashMap<String, Class<?>>(); props.put("foo", Integer.class); props.put("bar", String.class);
Let’s write a PojoGenerator, that dynamically generates a Class object for the given class name and a map, containing required properties:
import java.io.Serializable; import java.util.Map; import java.util.Map.Entry; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.CtField; import javassist.CtMethod; import javassist.NotFoundException; public class PojoGenerator { public static Class generate(String className, Map<String, Class<?>> properties) throws NotFoundException, CannotCompileException { ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.makeClass(className); // add this to define a super class to extend // cc.setSuperclass(resolveCtClass(MySuperClass.class)); // add this to define an interface to implement cc.addInterface(resolveCtClass(Serializable.class)); for (Entry<String, Class<?>> entry : properties.entrySet()) { cc.addField(new CtField(resolveCtClass(entry.getValue()), entry.getKey(), cc)); // add getter cc.addMethod(generateGetter(cc, entry.getKey(), entry.getValue())); // add setter cc.addMethod(generateSetter(cc, entry.getKey(), entry.getValue())); } return cc.toClass(); } private static CtMethod generateGetter(CtClass declaringClass, String fieldName, Class fieldClass) throws CannotCompileException { String getterName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); StringBuffer sb = new StringBuffer(); sb.append("public ").append(fieldClass.getName()).append(" ") .append(getterName).append("(){").append("return this.") .append(fieldName).append(";").append("}"); return CtMethod.make(sb.toString(), declaringClass); } private static CtMethod generateSetter(CtClass declaringClass, String fieldName, Class fieldClass) throws CannotCompileException { String setterName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); StringBuffer sb = new StringBuffer(); sb.append("public void ").append(setterName).append("(") .append(fieldClass.getName()).append(" ").append(fieldName) .append(")").append("{").append("this.").append(fieldName) .append("=").append(fieldName).append(";").append("}"); return CtMethod.make(sb.toString(), declaringClass); } private static CtClass resolveCtClass(Class clazz) throws NotFoundException { ClassPool pool = ClassPool.getDefault(); return pool.get(clazz.getName()); } }
That’s it!
Using PojoGenerator is quite simple. Let’s generate some POJO, output via reflection all its methods, set and then get some property:
public static void main(String[] args) throws Exception { Map<String, Class<?>> props = new HashMap<String, Class<?>>(); props.put("foo", Integer.class); props.put("bar", String.class); Class<?> clazz = PojoGenerator.generate( "net.javaforge.blog.javassist.Pojo$Generated", props); Object obj = clazz.newInstance(); System.out.println("Clazz: " + clazz); System.out.println("Object: " + obj); System.out.println("Serializable? " + (obj instanceof Serializable)); for (final Method method : clazz.getDeclaredMethods()) { System.out.println(method); } // set property "bar" clazz.getMethod("setBar", String.class).invoke(obj, "Hello World!"); // get property "bar" String result = (String) clazz.getMethod("getBar").invoke(obj); System.out.println("Value for bar: " + result); }
Executing above will result in the following console output:
Clazz: class net.javaforge.blog.javassist.Pojo$Generated Object: net.javaforge.blog.javassist.Pojo$Generated@55571e Serializable? true public void net.javaforge.blog.javassist.Pojo$Generated.setBar(java.lang.String) public java.lang.String net.javaforge.blog.javassist.Pojo$Generated.getBar() public java.lang.Integer net.javaforge.blog.javassist.Pojo$Generated.getFoo() public void net.javaforge.blog.javassist.Pojo$Generated.setFoo(java.lang.Integer) Value for bar: Hello World!