How to get bytecode of cglib proxy class instance?

1.3k views Asked by At

I'm trying to get bytecode of cglib enhanced object this way using BCEL:

package app;

import cglib.MyInterceptor;
import net.sf.cglib.proxy.Enhancer;
import org.apache.bcel.Repository;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import service.Tool;

public class CgLibApp {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        // target object
        Tool tool = new Tool();

        // proxying
        Enhancer e = new Enhancer();
        e.setSuperclass(tool.getClass());
        e.setCallback(new MyInterceptor(tool));
        Tool proxifiedTool = (Tool) e.create();

        // trying to get proxy byte code
        JavaClass clazz = Repository.lookupClass(proxifiedTool.getClass());
        Method method = clazz.getMethod(Tool.class.getMethod("meth"));

        System.out.println(method.getCode().toString());
    }
}

But I'm getting:

Exception in thread "main" java.lang.ClassNotFoundException: SyntheticRepository could not load service.Tool$$EnhancerByCGLIB$$22a3afcc
at org.apache.bcel.util.SyntheticRepository.loadClass(SyntheticRepository.java:174)
at org.apache.bcel.util.SyntheticRepository.loadClass(SyntheticRepository.java:158)
at org.apache.bcel.Repository.lookupClass(Repository.java:74)
at app.CgLibApp.main(CgLibApp.java:21)

What should I do to get bytecode from Enhanced object?

3

There are 3 answers

0
Rafael Winterhalter On

BCEL queries a class loader for a .class file in order to get hold of the byte array that represents it. Such a class file does not exist for a dynamically generated class.

In order to get hold of the class file, you have to collect the byte code during the class file's creation. Cglib is built on top of ASM and it allows you to register your own ClassVisitors to collect a class file.

With the Enhancer, use the generateClass(ClassVisitor) method and hand the latter method a ClassWriter. After calling the method, you can get the byte code from the class writer object that you passed.

0
NagararajaPALLA On

here is the sample code to print pseudo code of generated CGLIB class. visitEnd method prints the generated class in text format.

package naga.cglib.demo;

import static org.objectweb.asm.Opcodes.ASM7;

import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.util.TraceClassVisitor;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.FixedValue;

public class App {
public static void main(String[] args) throws Exception, IllegalArgumentException, InvocationTargetException {

    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(SampleClass.class);
    enhancer.setCallback(new FixedValueImpl());
    SampleClass proxy = (SampleClass) enhancer.create();
    enhancer.generateClass(new CustomClassWriter());

    System.out.println("Hello cglib!" + proxy.test(null));

}
}

class SampleClass {
public String test(String input) {
    return "Hello world!";
}
}

class FixedValueImpl implements FixedValue {

@Override
public Object loadObject() throws Exception {
    // TODO Auto-generated method stub
    return "Hello cglib! from loadObject()";
}

}

class CustomClassWriter extends ClassVisitor {

TraceClassVisitor tracer;
PrintWriter pw = new PrintWriter(System.out);

public CustomClassWriter() {
    super(ASM7);
    tracer = new TraceClassVisitor(pw);
}

@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
    System.out.println("method name is :" + name);
    return tracer.visitMethod(access, name, desc, signature, exceptions);

}

@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {

    System.out.println("field name is :" + name);
    return tracer.visitField(access, name, desc, signature, value);
}

public void visitEnd() {
    tracer.visitEnd();
    System.out.println(tracer.p.getText());

}
}
0
seregamorph On

I've found this question while researching how to save the CGLIB-generated class in spring-boot 3.0 application (e.g. handling @Transactional or @Configuration-annotated classes). This simple approach may help:

import org.springframework.cglib.core.ReflectUtils;
...
public class SpringCglibUtils {
    public static void initGeneratedClassHandler(String targetPath) {
        File dir = new File(targetPath);
        dir.mkdirs();
        ReflectUtils.setGeneratedClassHandler((String className, byte[] classContent) -> {
            try (FileOutputStream out = new FileOutputStream(new File(dir, className + ".class"))) {
                out.write(classContent);
            } catch (IOException e) {
                throw new UncheckedIOException("Error while storing " + className, e);
            }
        });
    }
}

and then define in your main class before creating context:

SpringCglibUtils.initGeneratedClassHandler("cglib");

Spring will store to the targetPath directory all generated class files.

Note: unfortunately it's not available before spring-boot 3