我正在尝试使用bcel以这种方式获得cglib增强对象的字节:
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());
}
}
但是我得到了:
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)
我该怎么做才能从增强对象获取字节码?
bcel对.class
文件的类加载程序查询,以保留代表它的字节数组。对于动态生成的类不存在这样的类文件。
为了掌握类文件,您必须在类文件的创建过程中收集字节代码。CGLIB建立在ASM之上,它允许您注册自己的ClassVisitor
S来收集类文件。
使用Enhancer
,使用generateClass(ClassVisitor)
方法并将后一种方法a ClassWriter
。调用该方法后,您可以从经过的类作者对象中获取字节代码。
这是打印生成CGLIB类的伪代码的示例代码。访问方法以文本格式打印生成的类。
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());
}
}
我在研究如何保存cglib生成的类3.0应用程序时找到了这个问题(例如,处理@Transactional
或@Configuration
宣布类)。这种简单的方法可能会有所帮助:
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);
}
});
}
}
然后在创建上下文之前在主类中定义:
SpringCglibUtils.initGeneratedClassHandler("cglib");
Spring将存储到targetPath
目录所有生成的类文件。
注意:不幸的是,它在弹簧靴3