2014-11-26 5 views
0

저는 현재 사용자 정의 ORM 프레임 워크를 개발 중이며 ASM을 사용하여 런타임시 하위 클래스를 동적으로 생성합니다. 생성 과정은 정상적으로 완료된 것으로 보이지만 결과 클래스를 인스턴스화하려고하면 "NoClassDefFoundError"가 발생합니다.런타임에 서브 클래스를 동적으로 생성합니다.

오류는 실제 하위 클래스가 아니라 Super 클래스와 관련이있는 것 같습니다.

/**loads the generated proxy class from the provided bytes. */ 
private Class loadProxyClass(byte[] aGeneratedProxyClass,String proxyClassName) throws ClassNotFoundException{ 
    return new ProxyClassLoader(Thread.currentThread().getContextClassLoader(),aGeneratedProxyClass) 
       .loadClass(this.convertToExternalName(proxyClassName)); 
} 
:

private Class generateProxyClass(Class anEntitySuperClass, 
           List<Field> fieldsToIntercept) throws ClassNotFoundException{ 
    String entitySuperClassName = this.convertToInternalName(anEntitySuperClass.getName()); 
    //String entityProxySubClassName = "com/flux/dynamic/".concat(anEntitySuperClass.getSimpleName()).concat("Proxy"); 
    String entityProxySubClassName = anEntitySuperClass.getSimpleName().concat("Proxy"); 

    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); 
    cw.visit(V1_6,ACC_PUBLIC+ACC_SUPER,entityProxySubClassName,null,entitySuperClassName,null); 
    cw.visitSource(entityProxySubClassName.concat(".java"),null); 

    //create constructor 
    MethodVisitor mv = cw.visitMethod(ACC_PUBLIC,"<init>","()V",null,null); 
    mv.visitCode(); 
    //have our consturctor initailise its super class. 
    mv.visitVarInsn(ALOAD,0); 
    mv.visitMethodInsn(INVOKESPECIAL,entitySuperClassName,"<init>","()V"); 
    mv.visitInsn(RETURN); 
    mv.visitMaxs(0,0); 
    mv.visitEnd(); 

    this.generateAndAppendProxyAccessorMethods(anEntitySuperClass,fieldsToIntercept, cw); 
    cw.visitEnd(); 
    //at this point our class should be fully generated an include all required fields. next we 
    //convert the class to a byte array and pass it in to our helper method to load an 
    //actual class from the byte array. 
    return this.loadProxyClass(cw.toByteArray(),entityProxySubClassName); 
} 

위라는 "loadProxyClass"방법은 기본적으로 동적으로 생성 된 클래스를로드하기 위해 사용자 정의 클래스 로더를 을 인스턴스화하고 호출하는 도우미 메서드입니다 : 여기에 서브 클래스 생성 방법에서 발췌 한 것입니다

ProxyClassLoader은 단순히 클래스 로더를 확장하고 동적으로 생성 된 클래스 바이트를로드하기 위해 "(때문에) findClass"메소드를 오버라이드 (override) :

public class ProxyClassLoader extends ClassLoader { 

    private byte[] rawClassBytes; 

    public ProxyClassLoader(ClassLoader parentClassLoader,byte[] classBytes){ 
    super(parentClassLoader); 
    this.rawClassBytes = classBytes; 
    } 

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

내가 오류는 다음과 같습니다 Exception in thread "main" java.lang.NoClassDefFoundError: DummyEntity (wrong name: DummyEntityProxy)

DummyEntity 내가 generateProxyClass 방법으로 통과 슈퍼 클래스입니다

하고 DummyEntityProxy 내가 생성을 시도하고있어 클래스입니다. 나는 도움을 청하게 될 것입니다. 클래스 DummyEntity를로드 할 것으로 예상

Exception in thread "main" java.lang.NoClassDefFoundError: DummyEntity (wrong name: DummyEntityProxy)

클래스 로더를하지만 링크 된 자원은 DummyEntityProxy라는 이름의 클래스를 포함 :

답변

0

의견을 보내 주셔서 감사합니다. 여러 시간 동안 땜질을하고 난 후에 나는 그 오류를 해결할 수 있었다. 이 오류는 방법에 기인 한 것으로 보인다 :

this.generateAndAppendProxyAccessorMethods(anEntitySuperClass,fieldsToIntercept, cw); 

이보다 구체적으로,이 방법에 의해 생성 된 코드 중 일부는 잘못 단순한 이름이 아니라 내부의 정규화 된 클래스 이름으로 슈퍼 클래스를 참조. 간결함을 위해 제 질문에서이 메소드의 구현을 생략했으며 또한이 메소드가이 문제점과 관련되어 있음을 진정으로 예상하지 않았기 때문에 생략되었습니다. 일반적으로 동적으로 생성 된 바이트 코드 논리에 오류가 발생하면 JVM 오류 메시지가 너무 모호하기 때문에 원인을 정확히 찾아내는 것이 매우 어려울 수 있습니다.

+0

추가 참조 용 : Java 8에서는 검증 자 오류 메시지가 훨씬 쉽게 읽을 수있게되었습니다. –

2

문제는 예외의 메시지를 통해 공개된다. 어떻게 그럴 수 있죠?

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

당신은로드 할 시도했지만 당신이 알고있는 유일한 클래스는 DummyEntityProxy의 바이트 표현으로 name의 클래스를 정의 어떤 클래스를 구분하지 않습니다 : 당신의 클래스 로더의 findClass 메소드의 구현입니다. 오히려 다음을 구현하십시오.

@Override 
public Class findClass(String name) { 
    if (!name.equals(entityProxySubClassName)) { 
    throw new ClassNotFoundException(name); 
    } 
    return defineClass(name, this.rawClassBytes, 0, this.rawClassBytes.length); 
} 

이렇게하면 다른 이름의 클래스를 정의하지 않게됩니다. 그러나 처음에는 클래스에 대해 ProxyClassLoader을 쿼리해서는 안되지만 부모 중 하나가 성공적으로이를 확인해야하는 것처럼 보입니다.

ASM은 사용자의 필요에 따라 상당히 낮은 수준의 API 인 것처럼 보입니다. 예를 들어 내 라이브러리 Byte Buddy과 같이 더 높은 수준의 API를 고려 했습니까? Hibernate 나 Eclipse 링크와 같은 다른 ORM도 그 수준에서 API를 사용합니다. 어려움을 겪고있는 것이 옳지 않기 때문입니다.

+0

의견을 보내 주셔서 감사합니다. 오류는 클래스로드와 관련이 없으므로 참조 된 하위 클래스 이름이 잘못 지정 되었기 때문에 발생했습니다. (즉, 나는 완전한 이름의 내부 클래스 이름 대신에 단순한 이름을 사용했다.) - 나는 더 이상 안된다. –

+0

나는 ASM에 대해 당신의 요지를 가지고있다. 그러나 나는 사실 그것이 그렇게 낮은 수준이라면 완전한 통제를 제공한다는 사실을 좋아한다. 자바 클래스의 생성/계측을 통해 지금까지, 나는 6 개 이상의 프로젝트에 그것을 사용했다. –

+0

@GilesThompson 그러나 여전히 어려운 시나리오가 있습니다. 그 단지 팁. ASM은 훌륭하지만, 일이 깨지기 쉽고 항상 생각하지 못한 구석이 있습니다. –

2

일반적으로 묻는 질문에 관계없이 동일한 클래스를 반환하려고 시도하는 ClassLoader을 구현하는 것은 좋지 않습니다. 이것은 당신이 얻는 오류로 완벽하게 설명됩니다 : NoClassDefFoundError: DummyEntity (wrong name: DummyEntityProxy). 시스템에서 ClassLoaderDummyEntity 클래스를 요청하고 DummyEntityProxy이라는 클래스를 반환했습니다.

일반적으로 상위 로더가 먼저 질문하는 것처럼 로더가 해당 클래스를 요청한 이유는 다음과 같습니다. 부모 로더가 사용하고있는 상위 클래스 로더 (Thread.currentThread().getContextClassLoader())가 수퍼 클래스에 액세스 할 수 없음을 나타내는 수퍼 클래스를 찾지 못한 것으로 보입니다.부모 로더로 anEntitySuperClass.getClassLoader()을 사용하면 더 쉬울 것입니다.

물론 생성 된 프록시에서 사용하는 다른 모든 클래스에 anEntitySuperClass의 클래스 로더가 액세스 할 수 있도록해야합니다. 그렇지 않은 경우 두 그룹 클래스를 모두 사용할 수 있도록 매우 복잡한 로더 위임 구조가 필요할 수 있습니다. 불가능할 수도 있습니다 (프록시가 실제로해야 할 일에 달려 있습니다).

+0

당신의 제안에 대해 대단히 고마워, 그 오류는 클래스에서 동적으로 생성 된 일부 메소드와 관련이있는 것처럼 보입니다. 문제의 메소드는 관련 수퍼 클래스를 내부 표준 클래스 이름이 아닌 단순 이름으로 잘못 참조했다. –

관련 문제