2017-01-11 12 views
1

Я смотрю код ByteBuddy от кого-то другого. Он использует ByteBuddy для создания подклассов времени выполнения, которые используются как прокси-серверы для реализации некоторого кода управления его средой выполнения в конкретных объектах.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); 

Для того, чтобы не связывать core объект непосредственно в лямбда Я хочу использовать новый Defined поле _core таким образом я могу повторно использовать сгенерированный класс (а не регенерировать его для каждого вызова функции). Есть ли способ достичь этого?

Заранее спасибо.

ответ

1

Вы можете определить пользовательские конструкторы так же, как вы определяете методы. Одним из важных моментов для определения конструктора является то, что вам требуется другой вызов конструктора в качестве его первой инструкции. Вы можете вызвать конструктор, используя MethodCall::invoke, который вы можете комбинировать с FieldAccessor::ofField.

Таким образом, вы можете определить свой класс, подобный следующему:

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's TypeCache.

+0

Спасибо за подсказку w.r.t. кэширование. Я бы использовал 'WeakHashMap , класс >' else. – lschuetze

+1

Это не работает, поскольку значение является подклассом ключа и сильно ссылается на него. Скорее используйте 'TypeCache', который работает вокруг этого, также ссылаясь на значение мягко или слабо. –

+0

Мне все еще не ясно, как я могу получить доступ к новому аргументу '_core' в' .intercept (InvocationHandlerAdapter.of ((proxy, method, m_args) -> proxyHandler (core, method, m_args); ' – lschuetze