2017-01-11 12 views
1

다른 ByteBuddy 코드를보고 있습니다. 그는 ByteBuddy를 사용하여 런타임의 특정 관리 코드를 특정 객체에 구현하기 위해 프록시로 사용되는 런타임 하위 클래스를 생성합니다. I 이렇게 I은 생성 된 클래스를 재사용 (및 기능의 모든 호출을 위해 재생성되지 않음) 할 수있는 새로운 정의 필드 _core을 사용할 람다에 직접 core 객체 결합하지 위해서는ByteBuddy : 클래스 생성시 절편에서 새 정의 필드 사용

Class<? extends T> newSubClass = new ByteBuddy(ClassFileVersion.ofThisVm()) 
       .subclass(classType) 
       .defineField("_core", Object.class, Visibility.PUBLIC) //<--- 
       .method(ElementMatchers.isDeclaredBy(classType)) 
       .intercept(InvocationHandlerAdapter.of((proxy, method, m_args) -> { 
        //TODO: Need to replace core with _core as core is a function argument and will make it bound 
        return proxyHandler(core, method, m_args); //<-- 
       })) 
       .make() 
       .load(roleType.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER) 
       .getLoaded(); 


T proxy = ReflectionHelper.newInstance(newSubClass, args); 
newSubClass.getField("_core").set(proxy, core); 

. 이 방법이 있습니까?

미리 감사드립니다.

답변

1

메서드를 정의하는 것처럼 사용자 지정 생성자를 정의 할 수 있습니다. 생성자를 정의 할 때 중요한 점 중 하나는 첫 번째 명령어로 다른 생성자 호출이 필요하다는 것입니다. FieldAccessor::ofField과 결합 할 수있는 MethodCall::invoke을 사용하여 생성자를 호출 할 수 있습니다.

이 방법, 당신은 다음과 같은 클래스를 정의 할 수 있습니다 :

new ByteBuddy(ClassFileVersion.ofThisVm()) 
    .subclass(classType, ConstructorStrategy.Default.NO_CONSTRUCTORS) 
    .defineConstructor(Visibility.PUBLIC) 
    .withParameter(InvocationHandler.class) 
    .intercept(MethodCall.invoke(classType.getDeclaredConstructor()) 
    .andThen(FieldAccessor.ofField("_core").setsArgumentAt(0))) 
    .defineField("_core", InvocationHandler.class, Visibility.PUBLIC) 
    .method(ElementMatchers.isDeclaredBy(classType)) 
    .intercept(InvocationHandlerAdapter.toField("_core")) 
    .make(); 

이 방법, 당신은 인스턴스 당 사용자 정의 InvocationHandler을 설정할 수 있습니다. 당신은 단지 _core 필드에 상태를 저장하고 인터셉터에서이 필드에 액세스하려면, MethodDelegation에서 봐 가지고 : 당신이해야 할 수도 있습니다

new ByteBuddy(ClassFileVersion.ofThisVm()) 
    .subclass(classType, ConstructorStrategy.Default.NO_CONSTRUCTORS) 
    .defineConstructor(Visibility.PUBLIC) 
    .withParameter(Object.class) 
    .intercept(MethodCall.invoke(classType.getDeclaredConstructor()) 
    .andThen(FieldAccessor.ofField("_core").setsArgumentAt(0))) 
    .defineField("_core", Object.class, Visibility.PUBLIC) 
    .method(ElementMatchers.isDeclaredBy(classType)) 
    .intercept(MethodDelegation.to(MyHandler.class)) 
    .make(); 

public class MyHandler { 
    @RuntimeType 
    public static Object intercept(@FieldValue("_core") Object value) { ... } 
} 

다른 주석 @This, @AllArguments, @Origin@SuperCall 있습니다. 덜 필요로할수록 프록시가 더 효율적입니다. 특히 @AllArguments은 할당 요구 사항 때문에 고가입니다.

이 경우, 귀하의 필드는 슈퍼 생성자 호출 후에 만 ​​설정된다는 점에 유의하십시오. 또한 수퍼 유형에 기본 생성자가 있다고 가정합니다. 또는 사용자 정의 ConstructorStrategy을 구현할 수 있습니다.

캐싱에 대해서는 Byte Buddy의 TypeCache을 살펴보십시오.

+0

힌트 w.r.t을 주셔서 감사합니다. 캐싱. 나는'WeakHashMap , Class >'else를 사용했을 것이다. – lschuetze

+1

값이 키의 하위 클래스이며이를 강력하게 참조하므로 작동하지 않습니다. 오히려 값을 부드럽게 또는 약하게 참조함으로써이 문제를 해결하는'TypeCache'를 사용하십시오. –

+0

'.intercept (InvokeHandlerAdapter.of ((proxy, method, m_args) -> proxyHandler (core, method, m_args);)의 새로운'_core' 인자에 접근 할 수있는 방법이 아직도 명확하지 않습니다. – lschuetze