JavaAgent+Javassist处理CGLIB动态类时抛出NotFoundException问题求助
Javassist处理CGLIB生成类时抛出NotFoundException异常
我在使用Javassist处理CGLIB生成的类时遇到了javassist.NotFoundException异常,使用JDK动态代理时一切正常,但CGLIB生成的代理类就会报错。相关代码如下:
1. pom.xml
<dependencies> <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.29.2-GA</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.7.7</version> <scope>compile</scope> </dependency> </dependencies>
2. CglibTest.java
public class CglibTest implements MethodInterceptor { public void run() { System.out.println("hello cglib"); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("before run()"); methodProxy.invokeSuper(o, objects); System.out.println("after run()"); return null; } }
3. Main.java
public class Main { public static void main(String[] args) { System.out.println("Hello world!"); CglibTest cglibTest = new CglibTest(); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(CglibTest.class); enhancer.setCallback(cglibTest); CglibTest testService = (CglibTest) enhancer.create(); System.out.println("CglibTest : " + testService.getClass().getName()); } }
4. JavaAgent + Javassist代码
public class Agent { public static void premain(String agentArgs, Instrumentation inst) { MonitorTransformer transformer = new MonitorTransformer(); inst.addTransformer(transformer, true); } } public class MonitorTransformer implements ClassFileTransformer { @Override public byte[] transform(ClassLoader loader, String originalClsName, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { String className = originalClsName.replaceAll("/", "."); if (className.startsWith("java.") || className.startsWith("sun.") || className.startsWith("jdk.") || className.startsWith("javax.") || className.startsWith("com.sun.")) { return classfileBuffer; } ClassPool ctPool = ClassPool.getDefault(); CtClass ctClass; try { try { ctClass = ctPool.get(className); } catch (NotFoundException e) { ctPool.appendClassPath(new LoaderClassPath(loader)); try { ctClass = ctPool.get(className); } catch (NotFoundException ne) { System.out.println("javassist fail, ctClass not found: " + className + ", origin: " + originalClsName); ne.printStackTrace(); return classfileBuffer; } } return ctClass.toBytecode(); } catch (Error e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return classfileBuffer; } @Override public byte[] transform(Module module, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { ClassLoader cl = module.getClassLoader() == null ? loader : module.getClassLoader(); return this.transform(cl, className, classBeingRedefined, protectionDomain, classfileBuffer); } }
运行后抛出的异常:
javassist.NotFoundException: org.example.CglibTest$$EnhancerByCGLIB$$f3005a82
问题原因
CGLIB生成的代理类是仅存在于内存中的动态类,没有对应的物理.class文件存放在类路径下。而Javassist的ClassPool.getDefault()默认只会从系统类路径、扩展类路径等标准位置加载类定义,无法直接获取到CGLIB动态生成的类信息。
解决方法
修改MonitorTransformer的逻辑,直接利用transform方法传入的classfileBuffer(内存中的类字节码)创建CtClass,无需依赖类路径加载:
public class MonitorTransformer implements ClassFileTransformer { private static final ClassPool ctPool = ClassPool.getDefault(); @Override public byte[] transform(ClassLoader loader, String originalClsName, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { String className = originalClsName.replaceAll("/", "."); if (className.startsWith("java.") || className.startsWith("sun.") || className.startsWith("jdk.") || className.startsWith("javax.") || className.startsWith("com.sun.")) { return classfileBuffer; } try { // 直接通过内存字节码创建CtClass,跳过类路径查找 CtClass ctClass = ctPool.makeClass(new ByteArrayInputStream(classfileBuffer)); // 这里可以添加自定义的字节码增强逻辑(比如修改方法、添加埋点等) // ... byte[] modifiedBytecode = ctClass.toBytecode(); ctClass.detach(); // 释放CtClass缓存,避免内存泄漏 return modifiedBytecode; } catch (Error | Exception e) { e.printStackTrace(); } return classfileBuffer; } @Override public byte[] transform(Module module, ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { ClassLoader cl = module.getClassLoader() == null ? loader : module.getClassLoader(); return this.transform(cl, className, classBeingRedefined, protectionDomain, classfileBuffer); } }
额外优化建议
- 复用ClassPool:将ClassPool定义为静态成员,避免每次transform都重复初始化
- 释放CtClass:使用
ctClass.detach()清理Javassist的CtClass缓存,防止内存溢出 - 可选跳过代理类:如果不需要增强CGLIB代理类,可以通过判断类名是否包含
$$EnhancerByCGLIB$$直接跳过处理
内容的提问来源于stack exchange,提问作者Bill Jay
相关产品推荐
相关产品推荐

