After and before the constructor interceptor

I know how to create a BEFORE constructor interceptor:

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

I know how to create an AFTER constructor interceptor:

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

With the following interceptor:

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

However, I do not know how to create an interceptor before / after. Here is what I tried (naive approach based on what already worked for the methods):

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

Using this method, delegate:

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

With this setting, I get:

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

Full stack trace:

 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) 
+6
source share
1 answer

The Java virtual machine validator requires a hard-coded call to another constructor from any implemented constructor. Thus, using @SuperCall to implement environmental recommendations does not work, unfortunately. In fact, the @SuperCall annotation cannot work with alltogether constructors. (Ideally, Byte Buddy would catch this attempt and throw a more readable excepion, I will fix this for the next version of the library.)

What you can do is define an interceptor as follows:

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

using interception like:

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

This will call the before method first, then call the super constructor, and then call the after interceptor.

Of course, you may be able to transfer the value from before to after . For this, Byte Buddy does not yet offer a great way to do something. (I still hope to improve the JVM to take advantage of this. This VM limitation also affects people with method descriptors and is often referred to as unsuccessful disatvantage for people working with VMs.)

Currently, you can always define a ThreadLocal field in an interceptor, where you store the value from before that you read during after . (In a single-thread environment, you can even drop ThreadLocal and use a simple field.) One of the reasons I haven't set my sights on this yet is that most constructor hooks do not require participation. If you specify a more detailed use case, I may be able to help you in the future.

+2
source

All Articles