2015-12-23 66 views
6

我知道如何创建一个BEFORE构造拦截:后和构造拦截之前

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来实施around-advice不幸的是不起作用。事实上,@SuperCall注释不能与构造函数一起使用。 (理想情况下,字节好友会赶上这样的尝试,并抛出一个更具可读性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。为此,Byte Buddy尚未提供一种很好的做事方式。 (我仍然希望增强JVM本身的优势,这种VM限制也会影响到使用方法句柄的人,并且经常被虚拟机工作人员视为不幸的障碍)。

现在,您可以总是在拦截器中定义一个ThreadLocal字段,您在after期间读取的值为before。 (在单线程环境中,你甚至可以删除ThreadLocal并使用一个简单的字段。)我还没有针对这个目标的一个原因是大多数构造函数的截取并不需要周围的东西。如果您陈述更详细的用例,我可能会进一步帮助您。