You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
    }
}

额外优化建议

  1. 复用ClassPool:将ClassPool定义为静态成员,避免每次transform都重复初始化
  2. 释放CtClass:使用ctClass.detach()清理Javassist的CtClass缓存,防止内存溢出
  3. 可选跳过代理类:如果不需要增强CGLIB代理类,可以通过判断类名是否包含$$EnhancerByCGLIB$$直接跳过处理

内容的提问来源于stack exchange,提问作者Bill Jay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 03:25:03