Java ASM动态子类创建-NoClassDefFoundError BeanInfo

Java ASM动态子类创建-NoClassDefFoundError BeanInfo,java,class,dynamic,java-bytecode-asm,beaninfo,Java,Class,Dynamic,Java Bytecode Asm,Beaninfo,我正在尝试使用ASM框架动态创建一个子类。 我能够创建类并实例化它。但是当我试着去做的时候 org.apache.commons.beanutils.BeanUtils.copyProperties(processedEntity, entity); 它抛出以下异常: java.lang.NoClassDefFoundError: com/wheelsup/app/benefits/service/XpOWErhNBiBeanInfo (wrong name: com/wheelsup/ap

我正在尝试使用ASM框架动态创建一个子类。 我能够创建类并实例化它。但是当我试着去做的时候

org.apache.commons.beanutils.BeanUtils.copyProperties(processedEntity, entity); 
它抛出以下异常:

java.lang.NoClassDefFoundError: com/wheelsup/app/benefits/service/XpOWErhNBiBeanInfo (wrong name: com/wheelsup/app/benefits/service/XpOWErhNBi)
下面是我用来创建子类的代码:

Class<? extends T> get() throws Exception {
    String superClassInternalName = getInternalName(superClass);

    String subClassSimpleName = RandomStringUtils.random(10, true, false);
    String subClassInternalName = getClass().getPackage().getName().replaceAll("\\.", "/").concat("/").concat(subClassSimpleName);
    String subClassName = getClass().getPackage().getName().concat(".").concat(subClassSimpleName);

    ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
    classWriter.visit(Opcodes.V1_6,
            ACC_PUBLIC,
            subClassInternalName,
            null,
            superClassInternalName,
            null);

    visitDefaultConstructor(classWriter, superClassInternalName);

    classWriter.visitEnd();

    return SubClassLoader.<T>init().load(classWriter.toByteArray(), subClassName);
}

private void visitDefaultConstructor(ClassWriter classWriter, String superClassInternalName) {
    MethodVisitor methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
    methodVisitor.visitCode();
    methodVisitor.visitVarInsn(ALOAD, 0);
    methodVisitor.visitMethodInsn(INVOKESPECIAL, superClassInternalName, "<init>", "()V");
    methodVisitor.visitInsn(RETURN);
    methodVisitor.visitMaxs(0, 0);
    methodVisitor.visitEnd();
}

private static class SubClassLoader<T> {
    private final ClassLoader contextClassLoader;

    private SubClassLoader(ClassLoader contextClassLoader) {
        this.contextClassLoader = contextClassLoader;
    }

    static <U> SubClassLoader<U> init() {
        return new SubClassLoader<>(Thread.currentThread().getContextClassLoader());
    }

    @SuppressWarnings("unchecked")
    Class<? extends T> load(byte[] classBytes, String className) throws Exception {
        return (Class<? extends T>) new SubClassLoader.DynamicClassLoader(contextClassLoader, classBytes).loadClass(className);
    }

    private static class DynamicClassLoader extends ClassLoader {
        private byte[] rawClassBytes;

        private DynamicClassLoader(ClassLoader contextClassLoader, byte[] classBytes) {
            super(contextClassLoader);
            this.rawClassBytes = classBytes;
        }

        @Override
        public Class findClass(String name) {
            return defineClass(name, this.rawClassBytes, 0, this.rawClassBytes.length);
        }
    }
}

Class所以问题出在类加载器中

private static class SubClassLoader<T> {
    private final ClassLoader contextClassLoader;

    private SubClassLoader(ClassLoader contextClassLoader) {
        this.contextClassLoader = contextClassLoader;
    }

    static <U> SubClassLoader<U> init() {
        return new SubClassLoader<>(Thread.currentThread().getContextClassLoader());
    }

    @SuppressWarnings("unchecked")
    Class<? extends T> load(byte[] classBytes, String className) throws Exception {
        return (Class<? extends T>) new DynamicClassLoader(contextClassLoader, classBytes, className).loadClass(className);
    }

    private static class DynamicClassLoader extends ClassLoader {
        private byte[] classBytes;
        private final String className;

        private DynamicClassLoader(ClassLoader contextClassLoader, byte[] classBytes, String className) {
            super(contextClassLoader);
            this.classBytes = classBytes;
            this.className = className;
        }

        @Override
        public Class findClass(String className) throws ClassNotFoundException {
            if (StringUtils.equals(this.className, className)) {
                return defineClass(className, this.classBytes, 0, this.classBytes.length);
            }

            throw new ClassNotFoundException(className);
        }
    }
}
私有静态类子类加载器{
私有最终类加载器contextClassLoader;
私有子类加载器(类加载器上下文类加载器){
this.contextClassLoader=contextClassLoader;
}
静态子类装入器init(){
返回新的子类加载器(Thread.currentThread().getContextClassLoader());
}
@抑制警告(“未选中”)

类问题在于
findClass
实现尝试返回生成的类,而不管调用方请求哪个类。在某些情况下,无法加载类是使操作正常工作的正确方法

BeanUtils
类依赖于,它允许为检查的类执行可选的显式beaninfo实现,因此,如果被要求提供
Foo
beaninfo
,它将首先尝试加载一个类
FooBeanInfo
,如果失败,它将为
Foo
构造一个通用的beaninfo

但是,由于您的
findClass
实现试图(重新)以错误的名称
xpowerhnbieaninfo
构建
xpowerhnbieaninfo
类,而不是报告缺少
xpowerhnbieaninfo
,因此出现了问题

您必须更改
子类加载器
以接收所生成类的预期名称。然后,您可以将
findClass
实现更改为

@Override
public Class findClass(String name) throws ClassNotFoundException {
    if(!name.equals(expectedName))
        throw new ClassNotFoundException(name);
    return defineClass(name, this.rawClassBytes, 0, this.rawClassBytes.length);
}
一个更简单但有黑客攻击性的解决方案是,在第一个类构造之后,
null
取出
rawClassBytes
,并为每个后续类加载请求抛出一个
ClassNotFoundException
,作为继承的标准
loadClass
实现保证调用
findClass
只加载类,因此只要立即加载生成类的程序逻辑没有改变,所有后续请求都是关于不同的、不受支持的类


但是,由于关键点是程序逻辑不能更改,我不建议使用这种黑客、脆弱的解决方案。将生成的类的名称传递给自定义加载程序并进行验证,一开始代码要多一些,但要干净得多。

谢谢Holger!我在研究了类加载程序impl(即我们的类加载程序)后找到了解决方案在ByteBuddy框架中进行编辑,这正是您所说的。