2016-11-23 11 views
4

은 내가 런타임에서 전자를이 클래스의 사본을 얻을하고자하는 클래스를런타임에 Java 클래스의 복사본을 만드는 방법은 무엇입니까?

class Foo { 
    int increment(int x) { 
     return x + 1; 
    } 
} 

있습니다. 지.

class Foo$Copy1 { 
    int increment(int x) { 
     return x + 1; 
    } 
} 

같은 방법이지만 다른 이름을 가진 클래스입니다.

Proxy

는 모든 기관과 방법을을 위임 에 도움이 보인다,하지만 복사.

당신은 이것에 대한 Byte Buddy 사용할 수 있습니다
+2

왜 그렇게하고 싶습니까? 메소드가 인스턴스화되지 않습니다. 당신은 수업이 아니라 물건에 대해 말하는거야? –

+3

명백한 질문은 : 왜 이것을하고 싶습니까? 표준 클래스 로더를 통해로드 된 클래스를 원래 클래스 파일로 다시 변환 할 수는 없지만, 가능한 한 최선을 다해 클래스 로더를 작성하여'defineClass()'에 대한 호출을 가로 챌 수 있습니다. – biziclop

+0

@biziclop 복사 된 메서드 내에서 각 메서드 호출의 프로필을 깨끗한 단일형으로 유지하려고합니다. – leventov

답변

8

:이 방법은 Foo, 예를 들어, 내 클래스의 사용을 재정의하는 것이

Class<?> type = new ByteBuddy() 
    .redefine(Foo.class) 
    .name("Foo$Copy1") 
    .make() 
    .load(Foo.class.getClassLoader()) 
    .getLoaded(); 

Method method = type.getDeclaredMethod("increment", int.class); 
int result = (Integer) method.invoke(type.newInstance(), 1); 

주 메서드가 Foo을 반환하면 Foo$Copy1을 반환합니다. 모든 코드 참조가 동일합니다.

+0

감사합니다. 이 클래스는 private ClassLoader의'defineClass()'메소드를 사용합니까? 아니면 다른 방법입니까? – leventov

+0

두 번째 인수로'ClassLoadingStrategy'를 선택할 수 있습니다. 기본적으로 부트 스트랩 클래스 로더가 제공되지 않으면'Foo'의 클래스 로더에 대한 주입이 사용됩니다. 'ClassLoadingStrategy.Default.WRAPPER'를 사용하면, throw-away 클래스 로더를 생성 할 수 있습니다. –

0

바이트 코드에 정상적으로 액세스 할 수 있다면 안전하지 않을 수도 있습니다.

Foo.class.getClassLoader().getResourceAsStream()과 같은 것이 클래스의 바이트 코드를 제공 할 수 있습니다.

그런 다음 sun.misc.Unsafe.defineClass(String name, byte[] code, int off, int len, ClassLoader classLoader, ProtectionDomain protectionDomain)을 사용하여 Foo와 동일한 클래스 로더 및 보호 도메인에 클래스를 정의하지만 다른 이름으로 정의하십시오.

자세한 내용은 알아야하지만 타사 라이브러리가없는 가장 간단한 방법 일 수 있습니다.

+0

아마도 ByteBuddy 방식은 나중에 더 유연하고 쉽게 유지할 수 있습니다. –

+2

클래스 파일과 자체 참조에서 클래스 이름을 다시 정의해야합니다. 'Foo'라는 이름의 클래스 파일을 사용하는 것만으로는 효과가 없습니다. –

2
+0

아마 Javassist를 사용하는 사람들에게 적합한 솔루션 일 것입니다. 나는 Javassist와 Byte Buddy 사이에 처음에는 – leventov

+0

@leventov를 선호하지 않았고, JDK에서 BCEL을 사용할 수도 있습니다 (JDK에서 제공하지만 public API는 아닙니다. com.sun.org.apache.bcel.internal "패키지) Javassist에 의존하지 않고. – sozal

+0

like this ' String copyClassName = classToBeCopied.getName() + "$ Copy"; ClassGen classGen = new ClassGen (Repository.lookupClass (classToBeCopied)); classGen.setClassName (copyClassName); byte [] bytecode = classGen.getJavaClass(). getBytes(); 반송 UNSAFE.defineClass는 ( copyClassName, 바이트, bytecode.length, classToBeCopied.getClassLoader() classToBeCopied.getProtectionDomain()); ' – sozal