2015-12-23 18 views
6

wiem jak stworzyć PRZED konstruktora przechwytywania:Po i przed konstruktora kolektora

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

Umiem tworzyć PO konstruktora kolektora:

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

z następującymi kolektora:

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

Jednak nie wiem, jak utworzyć przechwytywacz przed/po. Oto, co starałem (naiwne podejście oparte na tym, co już pracował dla metod):

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

Dzięki tej metodzie delegata:

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()); 
} 

Przy tej konfiguracji uzyskać:

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

Pełny stos śledzenia:

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) 

Odpowiedz

2

Walidator wirtualnej maszyny języka Java wymaga zakodowanego na stałe innego inwentarza z dowolnego konstruktora, który jest zaimplementowany. Z tego powodu używanie @SuperCall do implementacji około-porady niestety nie działa. W rzeczywistości adnotacja @SuperCall nie może działać razem z konstruktorami. (Najlepiej, Byte Buddy by złapać tę próbę i rzucać bardziej czytelnej excepion będę naprawić dla kolejnej wersji biblioteki.)

Co można zrobić, jest zdefiniowanie przechwytywacza jak następuje:

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()); 
    } 
} 

stosując przechwycenie jak:

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

To będzie pierwszy wywołać metodę before, następnie wywołać super konstruktora, a następnie wywołać after przechwytujących.

Oczywiście możesz chcieć przetransportować wartość z before do after. W tym celu Bajt Buddy nie oferuje jeszcze świetnego sposobu robienia rzeczy. (Nadal mam nadzieję na ulepszenie w samej JVM, aby skorzystać z tej możliwości. To ograniczenie VM uderza również w ludzi posługujących się uchwytami metod i jest często wspomniane jako niefortunna przyczyna zniechęcenia przez osoby pracujące z VM.)

Na razie możesz zawsze należy zdefiniować pole ThreadLocal w przechwytywaczu, w którym zapisano wartość z before, którą odczytano podczas after. (W środowisku z jednym wątkiem można nawet upuścić ThreadLocal i użyć prostego pola.) Jedną z przyczyn, dla których nie podjąłem jeszcze tej decyzji, jest to, że większość wykrytych konstruktorów nie wymaga adware. Jeśli przedstawisz bardziej szczegółowy przypadek użycia, być może będę mógł Ci pomóc.

Powiązane problemy