2015-12-23 4 views
6

Я знаю, как создать ДО конструктора перехватчик:После и перед конструктором перехватчик

return builder.constructor(isDeclaredBy(typeDescription)) 
    .intercept(MethodDelegation.to(constructorInterceptor) 
    .andThen(SuperMethodCall.INSTANCE)); 

Я знаю, как создать ПОСЛЕ конструктора перехватчика:

return builder.constructor(isDeclaredBy(typeDescription)) 
    .intercept(SuperMethodCall.INSTANCE 
    .andThen(MethodDelegation.to(constructorInterceptor))); 

Со следующим перехватчика:

public void intercept(@Origin Constructor<?> constructor) { 
    System.out.println("intercepted " + constructor.getName()); 
} 

Однако я не знаю, как создать перехватчик до/после. Вот что я пытался (наивный подход, основанный на том, что уже работала методы):

return builder.constructor(isDeclaredBy(typeDescription)) 
    .intercept(MethodDelegation.to(constructorInterceptor)); 

С помощью этого метода делегата:

public void intercept(@Origin Constructor<?> constructor, @SuperCall Callable<?> zuper) throws Exception { 
    System.out.println("before " + constructor.getName()); 

    zuper.call(); 

    System.out.println("after " + constructor.getName()); 
} 

С помощью этой установки я получаю:

java.lang.ClassFormatError: Bad method name at constant pool index 23 in class file com/flow/agent/test/Foo$auxiliary$syFGNB3u 

Полных сток:

java.lang.IllegalStateException: Error invoking java.lang.ClassLoader#findClass 
    at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection$Dispatcher$Resolved.loadClass(ClassInjector.java:392) 
    at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection.inject(ClassInjector.java:201) 
    at net.bytebuddy.agent.builder.AgentBuilder$InitializationStrategy$SelfInjection$Dispatcher$Split.register(AgentBuilder.java:1017) 
    at net.bytebuddy.agent.builder.AgentBuilder$Default$Transformation$Simple$Resolution.apply(AgentBuilder.java:2795) 
    at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:3081) 
    at sun.instrument.TransformerManager.transform(TransformerManager.java:188) 
    at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428) 
    at java.lang.ClassLoader.defineClass1(Native Method) 
    at java.lang.ClassLoader.defineClass(ClassLoader.java:760) 
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) 
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:467) 
    at java.net.URLClassLoader.access$100(URLClassLoader.java:73) 
    at java.net.URLClassLoader$1.run(URLClassLoader.java:368) 
    at java.net.URLClassLoader$1.run(URLClassLoader.java:362) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at java.net.URLClassLoader.findClass(URLClassLoader.java:361) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424) 
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357) 
    ... 
Caused by: java.lang.ClassFormatError: Bad method name at constant pool index 23 in class file com/flow/agent/test/Foo$auxiliary$syFGNB3u 
    at java.lang.ClassLoader.defineClass1(Native Method) 
    at java.lang.ClassLoader.defineClass(ClassLoader.java:760) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:497) 
    at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection$Dispatcher$Resolved.loadClass(ClassInjector.java:388) 

ответ

2

Валидатор виртуальной машины Java требует жестко запрограммированного вызова другого конструктора из любого реализованного конструктора. Поэтому, используя @SuperCall для реализации рекомендаций, к сожалению, не работает. По сути, аннотация @SuperCall не может работать с конструкторами alltogether. (В идеале, Byte Buddy бы поймать эту попытку и бросить более читаемый excepion, я исправлю, что для следующей версии библиотеки.)

Что вы можете сделать, это определить перехватчик как следующее:

public class Interceptor { 
    public void before(@Origin Constructor<?> constructor) { 
    System.out.println("before " + constructor.getName()); 
    } 
    public void after(Origin Constructor<?> constructor) { 
    System.out.println("after " + constructor.getName()); 
    } 
} 

используя перехват как:

MethodDelegation.to(constructorInterceptor).filter(named("before")) 
       .andThen(SuperMethodCall.INSTANCE 
       .andThen(MethodDelegation.to(constructorInterceptor)) 
             .filter(named("after"))) 

Это первое вызовет метод before, а затем вызвать супер конструктор, а затем вызвать after перехватчик.

Возможно, вам потребуется перевозить значение от before до after. Для этого Байт Бадди еще не предлагает отличный способ сделать что-то. (Я все еще надеюсь на совершенствование JVM, чтобы воспользоваться этим. Это ограничение VM также поражает людей с помощью дескрипторов методов и часто упоминается как неудачное disatvantage для людей, работающих с VM.)

На данный момент вы можете всегда задайте поле ThreadLocal в перехватчике, где вы храните значение от before, которое вы читаете во время after. (В среде с одним потоком вы даже можете отказаться от ThreadLocal и использовать простое поле.) Одна из причин, по которой я еще не нацелился на это, заключается в том, что большинство перехватов конструктора не требуют вмешательства. Если вы укажете более подробный пример использования, я могу помочь вам в дальнейшем.

Смежные вопросы