2016-10-16 2 views
0

Я пытаюсь создать подкласс под динамическим использованием ASM Framework. Я могу создать класс и создать его экземпляр. Но когда я пытаюсь сделатьASM Dynamic Sub Class Creation - NoClassDefFoundError BeanInfo

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 вещь; что это? и как я могу решить свою проблему?

Спасибо.

ответ

0

Проблема в том, что ваша findClass реализация пытается вернуть один генерироваться класс, независимо от того, какой класс абонент просит. В некоторых ситуациях, не загружая класс, это правильная вещь, чтобы сделать операцию.

BeanUtils класса опирается на Introspector, что позволяет опциональному явный BeanInfo реализации для проверяемого класса, так что если спрашивают за BeanInfo из Foo, он будет пытаться загрузить класс FooBeanInfo первыми, и если это не удается, он будет строить общую информацию о бобах для Foo.

Но так как ваша 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); 
} 

Более простыми, но Hacky, решение будет null вне rawClassBytes после строительства первого класса и бросить ClassNotFoundException для каждого последующего запроса класса нагрузки как унаследованная стандартной loadClass реализация гарантирует вызывать findClass только для уже не загруженных классов, так как ваша программная логика сразу же загружает сгенерированный класс, не изменяется, все последующие запросы относятся к различным неподдерживаемым классам.

Однако, поскольку критическая точка заключается в том, что программная логика не должна меняться, я не рекомендую это хакерское, хрупкое решение. Передавая имя сгенерированного класса вашему пользовательскому загрузчику и проверяя его, сначала немного больше кода, но гораздо чище.

+0

спасибо Хольгер! Я нашел решение после того, как взглянул на классLoader impl, который используется в ByteBuddy Framework, который был именно тем, что вы сказали. – arammal

0

поэтому вопрос был в 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); 
     } 
    } 
} 
Смежные вопросы