前两天拜读深入Java虚拟机时, 看到书里有提及可以用CGLib动态生成类及类实例的做法, 就将CGLib下载下来一试.
众所周知, Java在运行时加载Class字节码到虚拟机中运行.
如下示例代码:
try {
URL url = new URL("file:/d:/test/lib/");
URLClassLoader urlCL = new URLClassLoader(new URL[] { url });
Class c = urlCL.loadClass("TestClassA");
TestClassA object = (TestClassA) c.newInstance();
object.method();
} catch (Exception e) {
e.printStackTrace();
}
那么当然也可以在运行时动态生成Class字节码并加载到JVM中. CGLib就是很经典的字节码动态生成工具.
本人做了简单的测试, 代码如下:
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
abstract class BaseGen {
protected List<String> calledMethods = new LinkedList<>(); // property
public int getRandomInt() { // Get a random integer
return new Random().nextInt(10000);
}
public abstract void printClassInfo(); // abstract method to print class info
}
class DynamicGen implements MethodInterceptor {
public static <T> T newInstance(Class<? extends BaseGen> clazz) {
Enhancer e = new Enhancer();
e.setSuperclass(clazz);
e.setCallback(new DynamicGen());
return (T) e.create(); // Generate a class object
}
@Override
public Object intercept(Object obj, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
Object ret = null;
String name = method.getName();
if (name.equals("getRandomInt")) { // intercept the getRandomInt
ret = methodProxy.invokeSuper(obj, params); // call the base method
System.out.println("Print random int: " + ret);
} else if (name.equals("printClassInfo")) { // implement the print class info method
System.out.println("Class: " + obj.getClass());
}
BaseGen baseGen = (BaseGen) obj;
baseGen.calledMethods.add(name); // change the property
return ret;
}
}
public class GenCodeTest {
public static void main(String[] args) {
BaseGen worker = DynamicGen.newInstance(BaseGen.class); // generate a BaseGen instance
int random = worker.getRandomInt();
System.out.println("You can get an int: " + random); // call the method
worker.printClassInfo(); // print instance class info
System.out.println("Called injected methods: " + worker.calledMethods); // print the property
}
}