byte-buddy icon indicating copy to clipboard operation
byte-buddy copied to clipboard

How to ignore interfaces and match only based on method names and parameters

Open guru1306 opened this issue 2 years ago • 3 comments

Thanks for creating bytebuddy. It's a fantastic library. I am moving a legacy cglib implementation to bytebuddy to work with JDK 17. I am using latest bytebuddy version 1.12.9

There are two classes here: Source and Target. Bytebuddy is used to create proxy for Source and redirect to Target's methods if the Source 's method is abstract

OpenConnection Interface has Method Double open(int i ) => Not implemented anywhere

SmallOpenConnection Interface has below methods Double open(int i ) => Implemented by Target

public abstract class Source implements OpenConnection {} public class Target implements SmallOpenConnection{ public Double open(int i){ return new Double(4); } }

When I call proxy.Open(4), though the method name is same bytebuddy is failing with the below error. The methods belong to different interface but they have same names and parameter types. Is there a way to ignore the interface and use only method name and parameters for matching?

Exception in thread "main" java.lang.ClassCastException: com.bytebuddy.demo.Target cannot be cast to com.bytebuddy.demo.OpenConnection at com.bytebuddy.demo.Source$ByteBuddy$B7CASAjs$auxiliary$X26c7uMO.to(Unknown Source) at com.bytebuddy.demo.Interceptor.log(Interceptor.java:19) at com.bytebuddy.demo.Source$ByteBuddy$B7CASAjs.open(Unknown Source) at com.bytebuddy.demo.Starter.test(Starter.java:23) at com.bytebuddy.demo.Starter.main(Starter.java:16) T proxy = new ByteBuddy() .subclass(source) .method(isAbstract()) .intercept(MethodDelegation.withDefaultConfiguration() .withBinders(Pipe.Binder.install(Forwarder.class)) .to(new Interceptor(tgt))) .make() .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.INJECTION) .getLoaded().newInstance();

`public class Interceptor {

private final Object adaptee;

public Interceptor(Object adaptee) {
	super();
	this.adaptee = adaptee;
}

@RuntimeType
public Object log(@Pipe Forwarder<Object, Object> pipe) {
	System.out.println("Calling method");
	try {
		return pipe.to(adaptee);
	} finally {
		System.out.println("Returned from method");
	}
}

} `

guru1306 avatar Apr 15 '22 16:04 guru1306

Unfortunately, there is nothing ready made, but I would suggest you to get the @Origin Method and resolve the target using reflection and cache it in a concurrent map.

Alternatively, you can write your own binder and register a custom annotation and proxy. Take the pipe binder as an example for it.

raphw avatar Apr 17 '22 19:04 raphw

Thanks. I am using reflection based interceptor for now

Is there any advantage in performance by having something similar to pipe binder?

guru1306 avatar Apr 18 '22 09:04 guru1306

It avoids allocation but mostly, in modern JVMs, the overhead is minimal. You might even use method handles to further reduce the overhead.

raphw avatar Apr 19 '22 15:04 raphw