首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >动态子类创建- NoClassDefFoundError BeanInfo

动态子类创建- NoClassDefFoundError BeanInfo
EN

Stack Overflow用户
提问于 2016-10-16 23:28:41
回答 2查看 248关注 0票数 1

我正在尝试使用ASM动态创建一个子类。我能够创建这个类并实例化它。但当我试着做

代码语言:javascript
复制
org.apache.commons.beanutils.BeanUtils.copyProperties(processedEntity, entity); 

它抛出此异常:

代码语言:javascript
复制
java.lang.NoClassDefFoundError: com/wheelsup/app/benefits/service/XpOWErhNBiBeanInfo (wrong name: com/wheelsup/app/benefits/service/XpOWErhNBi)

下面是我用来创建子类的代码:

代码语言:javascript
复制
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);
        }
    }
}

我不明白BeanInfo的意思,它是什么?我该如何解决我的问题?

谢谢。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-10-17 15:43:19

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

BeanUtils类依赖于允许被检查类的可选显式beaninfo实现的Introspector,因此如果被请求加载BeanInfo of Foo,它将尝试首先加载类FooBeanInfo,如果失败,它将为Foo构造一个泛型bean信息。

但是,由于您的findClass实现试图(重新)使用错误的名称( XpOWErhNBiBeanInfo )构造XpOWErhNBi类,而不是报告XpOWErhNBiBeanInfo的缺失,所以事情就出了问题。

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

代码语言:javascript
复制
@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,并为每个后续类加载请求抛出一个ClassNotFoundException,因为继承的标准loadClass实现保证只对尚未加载的类调用findClass,因此,只要立即加载生成类的程序逻辑没有改变,所有后续请求都涉及不同的、不受支持的类。

然而,由于关键的一点是程序逻辑不能改变,我不推荐那种笨拙、脆弱的解决方案。将生成的类的名称传递给您的自定义加载程序并对其进行验证,一开始代码要稍微多一些,但要简单得多。

票数 1
EN

Stack Overflow用户

发布于 2016-10-17 15:36:26

所以这个问题发生在ClassLoader

代码语言:javascript
复制
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);
        }
    }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40076623

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档