【问题标题】:ASM Dynamic Sub Class Creation - NoClassDefFoundError BeanInfoASM 动态子类创建 - NoClassDefFoundError BeanInfo
【发布时间】:2017-02-25 20:39:45
【问题描述】:

我正在尝试使用 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);
        }
    }
}

我不明白 BeanInfo 的东西;它是什么?以及如何解决我的问题?

谢谢。

【问题讨论】:

    标签: java class dynamic java-bytecode-asm beaninfo


    【解决方案1】:

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

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

    但是由于您的 findClass 实现尝试(重新)构造 XpOWErhNBi 类的错误名称 XpOWErhNBiBeanInfo,而不是报告不存在 XpOWErhNBiBeanInfo,因此出现问题。

    您必须更改您的SubClassLoader 才能接收生成的类的预期名称。然后,您可以将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 实现保证调用@ 987654340@ 仅针对尚未加载的类,因此只要您立即加载生成的类的程序逻辑没有改变,所有后续请求都是关于不同的、不受支持的类。

    但是,由于关键点是程序逻辑不能改变,所以我不推荐那种 hacky、脆弱的解决方案。将生成的类的名称传递给您的自定义加载器并对其进行验证,一开始需要更多代码,但更简洁。

    【讨论】:

    • 感谢霍尔格!在查看了 ByteBuddy 框架中使用的 ClassLoader impl 后,我找到了解决方案,这正是您所说的。
    【解决方案2】:

    所以问题出在 ClassLoader 中

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-29
      • 1970-01-01
      • 2012-03-13
      • 2012-08-03
      • 1970-01-01
      相关资源
      最近更新 更多