raphw / byte-buddy Goto Github PK
View Code? Open in Web Editor NEWRuntime code generation for the Java virtual machine.
Home Page: https://bytebuddy.net
License: Apache License 2.0
Runtime code generation for the Java virtual machine.
Home Page: https://bytebuddy.net
License: Apache License 2.0
Helpful for writing annotation processors would be an output stream and/or writer variant of DynamicType.saveIn(File)
.
I have a simple scenario where I receive an IllegalAccessError when loading a generated class that uses a NamingStrategy.
java.lang.IllegalAccessError: class FooBar.Foo cannot access its superclass org.modelmapper.FooTest$Foo
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
at java.lang.ClassLoader.defineClass(ClassLoader.java:634)
at net.bytebuddy.dynamic.loading.ByteArrayClassLoader.findClass(ByteArrayClassLoader.java:48)
at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
at net.bytebuddy.dynamic.ClassLoadingStrategy$Default.load(ClassLoadingStrategy.java:59)
at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:1132)
at org.modelmapper.FooTest.proxyClassFor(FooTest.java:35)
at org.modelmapper.FooTest.test(FooTest.java:40)
Here's the complete test:
@Test
public class FooTest {
static class Foo {
}
public void test() {
new ByteBuddy().withNamingStrategy(new NamingStrategy() {
public String getName(UnnamedType unnamedType) {
return "FooBar." + unnamedType.getSuperClass().getSimpleName();
}
})
.subclass(Foo.class)
.method(MethodMatchers.any())
.intercept(InvocationHandlerAdapter.of(new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
}))
.make()
.load(FooTest.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
}
}
All filterable lists have specific tests but the ParameterList
is missing.
While trying to workaround issue #10 I discovered that the following test cases results in only getter methods being intercepted.
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.ClassFileVersion;
import net.bytebuddy.NamingStrategy;
import net.bytebuddy.dynamic.ClassLoadingStrategy;
import net.bytebuddy.instrumentation.MethodDelegation;
import net.bytebuddy.instrumentation.method.bytecode.bind.annotation.AllArguments;
import net.bytebuddy.instrumentation.method.bytecode.bind.annotation.Origin;
import net.bytebuddy.instrumentation.method.bytecode.bind.annotation.RuntimeType;
import static net.bytebuddy.instrumentation.method.matcher.MethodMatchers.*;
import net.bytebuddy.utility.RandomString;
import org.testng.annotations.Test;
public class ServiceLocatorProxyNGTest {
@Test
public void testSomeMethod() throws InstantiationException, IllegalAccessException {
Class<ProxyedInterface> type = ProxyedInterface.class;
ClassLoader classLoader = getClass().getClassLoader();
Class<? extends ProxyedInterface> proxyType = new ByteBuddy(ClassFileVersion.JAVA_V8)
.withNamingStrategy(new ProxyNamingStrategy(type))
.subclass(type)
.method(isDeclaredBy(type))
.intercept(MethodDelegation.to(new GeneralInterceptor(new ProxyedInterfaceDelegate(10L))))
.make()
.load(classLoader, ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
ProxyedInterface result = proxyType.newInstance();
result.setObject(11l);
Long f = result.getObject();
System.out.println("");
}
public static interface ProxyedInterface {
void setObject(Long value);
Long getObject();
}
public static class ProxyedInterfaceDelegate implements ProxyedInterface {
private Long value;
public ProxyedInterfaceDelegate(Long value) {
this.value = value;
}
@Override
public void setObject(Long value) {
this.value = value;
}
@Override
public Long getObject() {
return value;
}
}
public class GeneralInterceptor {
private final ProxyedInterface delegate;
public GeneralInterceptor(ProxyedInterface delegate) {
this.delegate = delegate;
}
@RuntimeType
public Object intercept(@AllArguments Object[] allArguments, @Origin Method method) {
try {
System.out.println("Intercepted method: " + method.getName());
return method.invoke(delegate, allArguments);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
throw new RuntimeException(ex);
}
}
}
public static class ProxyNamingStrategy implements NamingStrategy {
private final Class<?> type;
public ProxyNamingStrategy(Class<?> type) {
this.type = type;
}
@Override
public String name(UnnamedType unnamedType) {
String packageName = type.getPackage().getName();
String simpleName = type.getSimpleName();
return String.format("test.%s_Proxy_%s", simpleName, RandomString.make());
}
}
}
Currenty, the annotations added to fields and methods of Implementation
instances are not added to the generated classes. This should be changed.
I have just started playing around with Buddy a couple of days ago.
My idea can roughly be described as dynamic beans generation: add field, getter, setter.
new ByteBuddy()
.subclass(Object.class)
.defineField("a", String.class, Visibility.PRIVATE)
.defineMethod("getA", Integer.TYPE, new ArrayList<Class<?>>(), Visibility.PUBLIC)
.intercept(FieldAccessor.ofField("a"))
.defineMethod("setA", Void.TYPE, args, Visibility.PUBLIC)
.intercept(FieldAccessor.ofField("a"))
.make()
.load(SilConfig.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
Note that the field is defined as String, getter method as Integer.
This leads to:
Exception in thread "main" java.lang.IllegalStateException
at net.bytebuddy.instrumentation.method.bytecode.stack.StackManipulation$Illegal.apply(StackManipulation.java:46)
at net.bytebuddy.instrumentation.method.bytecode.stack.StackManipulation$Compound.apply(StackManipulation.java:194)
at net.bytebuddy.instrumentation.method.bytecode.stack.StackManipulation$Compound.apply(StackManipulation.java:194)
at net.bytebuddy.instrumentation.method.bytecode.stack.StackManipulation$Compound.apply(StackManipulation.java:194)
at net.bytebuddy.instrumentation.FieldAccessor.apply(FieldAccessor.java:179)
at net.bytebuddy.instrumentation.FieldAccessor.applyGetter(FieldAccessor.java:110)
at net.bytebuddy.instrumentation.FieldAccessor$Appender.apply(FieldAccessor.java:790)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Entry$Simple.apply(TypeWriter.java:1062)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Engine$ForCreation.create(TypeWriter.java:695)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1150)
at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:219)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$AbstractDelegatingBuilder.make(DynamicType.java:1607)
Java version: 1.7.0_45
It took me a while to see what was wrong, due to the ambiguity of the exception thrown.
I need to create some abstract Mixin classes for a package of our framework persistent objects so I can use the jackson framework to serialize them. Are there any examples about how to create an abstract class so I can reference it in the JacksonObjectMapper as a Mixin Class?
objectMapper.addMixInAnnotations(POJO.class, POJOMixin.class);
Or am I screwed due to classloader issues?
Sample Class:
public abstract class JacksonItemMixin {
@JsonCreator
protected JacksonItemMixin(@JsonProperty("objId") final Long objId) {
}
}
The code I have so far:
Annotation methodAnnotation = JacksonItemMixin.class.getDeclaredConstructor(Long.class).getAnnotation(JsonCreator.class);
Annotation parameterAnnotation = JacksonItemMixin.class.getDeclaredConstructor(Long.class).getParameterAnnotations()[0][0];
Class<?> dynamicType = new ByteBuddy().withModifiers(TypeManifestation.ABSTRACT)
.subclass(Object.class, ConstructorStrategy.Default.NO_CONSTRUCTORS)
.name(declaringClass.getCanonicalName().concat("MixIn"))
.defineConstructor(Arrays.<Class<?>>asList(Long.class), Visibility.PROTECTED)
.withoutCode()
.annotateMethod(methodAnnotation)
.annotateParameter(0, parameterAnnotation)
.make()
.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
.getLoaded();
dynamicType.newInstance();
} catch (NoSuchMethodException e) {
e.printStackTrace();
assert (false);
} catch (InstantiationException e) {
e.printStackTrace();
assert (true);
} catch (IllegalAccessException e) {
e.printStackTrace();
assert (false);
}
However this thows this Exception:
java.lang.IllegalStateException: Exception on invoking loader method
at net.bytebuddy.dynamic.loading.ClassLoaderByteArrayInjector.inject(ClassLoaderByteArrayInjector.java:142)
at net.bytebuddy.dynamic.loading.ClassLoaderByteArrayInjector.inject(ClassLoaderByteArrayInjector.java:107)
at net.bytebuddy.dynamic.ClassLoadingStrategy$Default$5.load(ClassLoadingStrategy.java:144)
at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:2944)
at de.idealo.core.factory.CachingItemFactoryTest.createMixinFor(CachingItemFactoryTest.java:293)
at de.idealo.core.factory.CachingItemFactoryTest.testClassScanning(CachingItemFactoryTest.java:277)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:211)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:67)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: java.lang.ClassFormatError: Method in class de/pinuts/core/CatalogItemMixIn has illegal modifiers: 0x404
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at net.bytebuddy.dynamic.loading.ClassLoaderByteArrayInjector$ClassLoadingAction.run(ClassLoaderByteArrayInjector.java:320)
at net.bytebuddy.dynamic.loading.ClassLoaderByteArrayInjector$ClassLoadingAction.run(ClassLoaderByteArrayInjector.java:290)
at java.security.AccessController.doPrivileged(Native Method)
at net.bytebuddy.dynamic.loading.ClassLoaderByteArrayInjector.inject(ClassLoaderByteArrayInjector.java:127)
... 32 more
`
Currently, matchers find elements in their resolved form what can be confusing as methods are defined in a different shape.
Th MethodDescription.Token
already defines the approriate logic to compare to methods based on their raw signature. It should not be duplicated in the special method invocation.
The J9 from IBM also has attach functionality,
it can be used the same way as the sun/oracle functionality, but it does not require the tools.jar and it is named differently,
check for example this attach from openjpa https://github.com/apache/openjpa/blob/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/InstrumentationFactory.java
simply use "com.ibm.tools.attach.VirtualMachine"
Currently, annotation types can be defined, it is however not possible to define a default value for an annotation.
I was unable to work out how to generate a final field and populate it in the synthetic constructor. After playing with the API I presume this is supported (is this right?). Improved documentation - example! - of beginning-to-end creating of immutable classes would be nice.
In general the API is quite good. Perhaps a high-level wrapper API would help?
When rebasing a class, instrumenting methods might require knowledge og the name of another method's super method. As these methods might or might not be instrumented themselfs, it is required to rebase methods that are themselfs not explicitly implemented. These methods should however be implicitly ignored in a first pass through in order to avoid writing these unneccessary methods.
It seems like a IntelliJ refactoring lead to a substitution of "representedBy" into many javadoc. This should be fixed.
The first sample works well with version 0.5.6 but the second sample throws exception for the version 0.6.5
package com.ui4j.test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.ClassLoadingStrategy;
import net.bytebuddy.instrumentation.MethodDelegation;
import net.bytebuddy.instrumentation.method.bytecode.bind.annotation.AllArguments;
import net.bytebuddy.instrumentation.method.bytecode.bind.annotation.Origin;
import net.bytebuddy.instrumentation.method.bytecode.bind.annotation.RuntimeType;
import net.bytebuddy.instrumentation.method.bytecode.bind.annotation.SuperCall;
import net.bytebuddy.instrumentation.method.bytecode.bind.annotation.This;
import net.bytebuddy.matcher.ElementMatchers;
import com.ui4j.webkit.proxy.WebKitProxy;
public class Main {
public static interface Element {
Element addClass(String... names);
Element removeClass(String... name);
}
public static class WebKitElement implements Element {
public WebKitElement() {
}
@Override
public Element addClass(String... names) {
return this;
}
@Override
public Element removeClass(String... name) {
return this;
}
}
public static class WebKitInterceptor {
@RuntimeType
public static Object execute(@SuperCall Callable<Object> callable,
@This Object that,
@Origin Method method,
@AllArguments Object[] arguments) {
try {
return callable.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) throws Exception {
Class<? extends WebKitElement> loaded = new ByteBuddy()
.subclass(WebKitElement.class)
.method(ElementMatchers.any()
.and(ElementMatchers.not(ElementMatchers.isDeclaredBy(Object.class))
.and(ElementMatchers.not(ElementMatchers.nameStartsWith("getEngine")))))
.intercept(MethodDelegation.to(WebKitInterceptor.class))
.make()
.load(WebKitProxy.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
Constructor<? extends WebKitElement> constructor = loaded.getConstructor();
WebKitElement instance = constructor.newInstance();
instance.addClass("foo", "bar");
}
}
package com.ui4j.test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import net.bytebuddy.implementation.bind.annotation.This;
import net.bytebuddy.matcher.ElementMatchers;
import com.ui4j.webkit.proxy.WebKitProxy;
public class Main {
public static interface Element {
Element addClass(String... names);
Element removeClass(String... name);
}
public static class WebKitElement implements Element {
public WebKitElement() {
}
@Override
public Element addClass(String... names) {
return this;
}
@Override
public Element removeClass(String... name) {
return this;
}
}
public static class WebKitInterceptor {
@RuntimeType
public static Object execute(@SuperCall Callable<Object> callable,
@This Object that,
@Origin Method method,
@AllArguments Object[] arguments) {
try {
return callable.call();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public static void main(String[] args) throws Exception {
Class<? extends WebKitElement> loaded = new ByteBuddy()
.subclass(WebKitElement.class)
.method(ElementMatchers.any()
.and(ElementMatchers.not(ElementMatchers.isDeclaredBy(Object.class))
.and(ElementMatchers.not(ElementMatchers.nameStartsWith("getEngine")))))
.intercept(MethodDelegation.to(WebKitInterceptor.class))
.make()
.load(WebKitProxy.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
Constructor<? extends WebKitElement> constructor = loaded.getConstructor();
WebKitElement instance = constructor.newInstance();
instance.addClass("foo", "bar");
}
}
Exception in thread "main" java.lang.VerifyError: Bad type on operand stack
Exception Details:
Location:
com/ui4j/test/Main$WebKitElement$ByteBuddy$x3pjoCmV.addClass([Ljava/lang/String;)Lcom/ui4j/test/Main$Element; @6: invokespecial
Reason:
Type '[Ljava/lang/String;' (current frame, stack[3]) is not assignable to 'java/lang/String'
Current Frame:
bci: @6
flags: { }
locals: { 'com/ui4j/test/Main$WebKitElement$ByteBuddy$x3pjoCmV', '[Ljava/lang/String;' }
stack: { uninitialized 0, uninitialized 0, 'com/ui4j/test/Main$WebKitElement$ByteBuddy$x3pjoCmV', '[Ljava/lang/String;' }
Bytecode:
0x0000000: bb00 2059 2a2b b700 212a b200 2404 bd00
0x0000010: 1559 032b 53b8 001b c000 1db0
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Unknown Source)
at java.lang.Class.getConstructor0(Unknown Source)
at java.lang.Class.getConstructor(Unknown Source)
at com.ui4j.test.Main.main(Main.java:69)
The github culture seems to have created an interesting twist to open source .. It is very very rare that I can find a pre-built project / library . The assumption seems to be that noone would want that ... ( this just a background comment, not specific to to BB but its relevent).
Fortuantly I did find byte-budy in the Maven repo ...
I do like cloning or forking projects so I can see the source, but prefer if I get a pre build 'official' libary, especially for Java where its inherently platform neutral. Otherwise its often significant effort and risk to duplicate a build properly (another assumption common on github - that everyone has the same tools and build environments, and knows how to pick the correct branches etc).
I'm learning ! and BB does have actual documentation on building (again rare!)
Very useful as for some reason Maven projects tend to confuse both me and Eclipse ...
I never know what the right command to use, what versions etc and Eclipse gets equally confused.
Thankful for your docs I did exactly this (after cloning from HEAD)
mvn package
it appeared to succeed
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] Byte Buddy (parent) ................................ SUCCESS [ 1.234 s]
[INFO] Byte Buddy Java agent .............................. SUCCESS [ 1.806 s]
[INFO] Byte Buddy (with dependencies) ..................... SUCCESS [ 8.480 s]
[INFO] Byte Buddy (without dependencies) .................. SUCCESS [ 0.142 s]
[INFO] Byte Buddy benchmarks .............................. SUCCESS [ 1.694 s]
[INFO] Byte Buddy for Android ............................. SUCCESS [ 0.921 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 14.397 s
[INFO] Finished at: 2015-01-17T09:35:42-05:00
[INFO] Final Memory: 28M/689M
[INFO] ------------------------------------------------------------------------
E:\Work\DEI\github\byte-buddy>
Except -- only the -dep version has any classes in it
E:\Work\DEI\github\byte-buddy\byte-buddy\target>ls -l
total 14
-rwxr-xr-x 1 DLEE None 5236 Jan 17 09:35 byte-buddy-0.6-SNAPSHOT.jar
E:\Work\DEI\github\byte-buddy\byte-buddy\target>jar t < byte-buddy-0.6-SNAPSHOT.jar
META-INF/
META-INF/MANIFEST.MF
META-INF/maven/
META-INF/maven/net.bytebuddy/
META-INF/maven/net.bytebuddy/byte-buddy/
META-INF/maven/net.bytebuddy/byte-buddy/pom.properties
META-INF/maven/net.bytebuddy/byte-buddy/pom.xml
META-INF/maven/net.bytebuddy/byte-buddy/pom.xml
META-INF/maven/net.bytebuddy/byte-buddy/pom.properties
E:\Work\DEI\github\byte-buddy\byte-buddy-dep\target>ls -l
total 1572
-rwxr-xr-x 1 DLEE None 1021281 Jan 17 09:35 byte-buddy-dep-0.6-SNAPSHOT.jar
META-INF/
META-INF/MANIFEST.MF
...
...
net/bytebuddy/utility/PropertyDispatcher$5.class
net/bytebuddy/utility/PropertyDispatcher$6.class
net/bytebuddy/utility/PropertyDispatcher$7.class
net/bytebuddy/utility/PropertyDispatcher$8.class
net/bytebuddy/utility/PropertyDispatcher$9.class
net/bytebuddy/utility/PropertyDispatcher.class
net/bytebuddy/utility/RandomString.class
net/bytebuddy/utility/StreamDrainer.class
META-INF/maven/net.bytebuddy/byte-buddy-dep/pom.xml
I tried a lot of guessing based on looking at help and what other packages ask for
(I dont know maven more then being able to type "mvn")
mvn install
mvn clean
mvn generate-sources
mvn build
a few dozen like that to no avail.
Eclipse M2 plugin didnt even get that far ...
I was just about to go ahead and use the -dep.jar when I did a little RTFM and found your great docs on NOT to do that ... but fortunately you also said its on Maven Central (had to poke around to find what URL that is) but successfully downloaded the non dep .jar and its working.
( pointing the source to my local git branch )
Any suggestions on what Im doing wrong ?
Im happy to try things and send you the results.
-David
Until Java 6, the Java reflection API represented non-generic arrays that were returned from generic-specific reflection as GenericArray
. This changed with Java 7. Byte Buddy should adopt this newer behavior. For this, generic array implementations should always check their component type.
Hi Rafael,
i just noticed that you use quite an obscure example in the 3-1 = 2 comparison example in the main readme.
It is correct, but it evokes a wtf moment when you see that it substracts hash codes. Is that intended, or shall I send a PR to make that less magical?
Currently, the ModifierResolver
is rather clumsy to use. Rather, it should allow for more general method transformations.
Why is there ByteBuddy#makeInterface(List)
instead of either:
ByteBuddy#makeInterface(Collection)
if you want to allow more argument typesByteBuddy#makeInterface(Iterable)
if you want to allow even more argument types, but not necessarily have a size()
method to improve ByteBuddyCommons#join(...)
performanceByteBuddy#makeInterface(Set)
if you want to indicate that each super-interface Class
should only be specified onceIs there a need to preserve the ordering of the input Class
elements?
I have a Set<Class>
that I'd like to pass in as an argument, but, with the existing makeInterface
methods, I need to convert it to either an array, or a List
, which seems unnecessary.
It's possible that List
arguments of other methods could also be converted, but I haven't looked over the whole Byte Buddy API.
Hi, I have a class where I have a final method. I used LoggerInterceptor as mentioned in you example. I cannot able to see the log for it. If I remove the final quantifier, I can able to see the log.
I have looked at a lot of ASM type libraries and was just starting on cglib when I ran into an issue with it and found your post about byte-buddy. Trying this out, I really like it, although its a bit of a learning curve to figure out model so I can do what I think is obvious - but has a different representation in bb.
One thing I am stuck on. I dont need this 100% but it would be very useful. Most ASM tools assume (many with comments saying so) that there is no use for creating interfaces, only classes.
I would like to be able to create interfaces, ideally root (not derived) interfaces.
With cglib that was in fact the first thing I tried and got to work (although I dont like that it derives from an internal interface, its not a top level one - but it is an interface)
I have tried about 20 ways to do to this with BB and scanned most of the source and and the web and cant find it. Two strategies that got close were
interface I {} ;
...
new ByteBuddy().subclass(I.class, no-constrors ).name(...)...
I got that to produce something that 'worked' but its a not an interface, its
class name extends Object implements I
I see in the code where its switching the subclass to Object ...
The other attempt is to use variations on this
new ByteBuddy()
.withModifiers(TypeManifestation.INTERFACE)
.subclass(Object.class,ConstructorStrategy.Default.NO_CONSTRUCTORS)
.name(name);
but that ends up crashing with null pointer exceptions in various places ...
the superclass isnt null but its contents are uninitialized.
Is this possible ? I cant find anywhere that constructs a Builder for either an iterface,
or (related probably) without a superclass.
The Annotation support looks like its close ...
I am curious if this is a fundamental difficulty with the JVM or ASM or if its simply something that noones ever asked for or thought of ..
I can live without it by making an abstract class but it seems a huge hole where the rest of the product has incredible depth.
-David
This is awesome. Is it possible to extent this so it can be easily used with other JVM languages with their idiomatic naming conventions. Especially Scala.
Currently, Byte Buddy does not validate if a user defines a class with illegal properties, e.g defines an abstract method on a non-abstract type, a default method on a Java 7 class etc.
I'm currently porting one of our libraries from CGLib to ByteBuddy and I've encounter a problem with @pipe. I've got a simple proxy that delegates all calls to the wrapped target object. Everything is OK until there is a method returning void on the target object.
My setup is like:
public class X {
public static class WrappedObject {
Object method1() {...}
void method2() {...}
}
public static interface Forwarder<T, S> {
T to(S target);
}
public static class Handler {
protected final WrappedObject wrappedObject;
public Handler(WrappedObject wrappedObject) {
this.wrappedObject = wrappedObject;
}
@RuntimeType
public Object delegate(@Pipe Forwarder<Object, WrappedObject> pipe) {
return pipe.to(wrappedObject);
}
}
public static void main(String... args) throws Exception {
WrappedObject proxiedObject = new ByteBuddy()
.subclass(WrappedObject.class)
.method(any())
.intercept(MethodDelegation.to(new Handler(new WrappedObject())).appendParameterBinder(Pipe.Binder.install(Forwarder.class)))
.make()
.load(X.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded()
.newInstance();
}
}
The proxy cannot be created:
Exception in thread "main" java.lang.IllegalStateException: An illegal stack manipulation must not be applied
at net.bytebuddy.instrumentation.method.bytecode.stack.StackManipulation$Illegal.apply(StackManipulation.java:46)
at net.bytebuddy.instrumentation.method.bytecode.stack.StackManipulation$Compound.apply(StackManipulation.java:194)
at net.bytebuddy.instrumentation.method.bytecode.bind.annotation.Pipe$Binder$Redirection$MethodCall$Appender.apply(Pipe.java:589)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Entry$Simple.apply(TypeWriter.java:1055)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Engine$ForCreation.create(TypeWriter.java:688)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1143)
at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:219)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$AbstractDelegatingBuilder.make(DynamicType.java:1611)
at net.bytebuddy.instrumentation.method.bytecode.bind.annotation.Pipe$Binder$Redirection.make(Pipe.java:332)
at net.bytebuddy.instrumentation.Instrumentation$Context$Default.register(Instrumentation.java:808)
at net.bytebuddy.instrumentation.method.bytecode.bind.annotation.Pipe$Binder$Redirection.apply(Pipe.java:342)
at net.bytebuddy.instrumentation.method.bytecode.bind.MethodDelegationBinder$ParameterBinding$Anonymous.apply(MethodDelegationBinder.java:192)
at net.bytebuddy.instrumentation.method.bytecode.bind.MethodDelegationBinder$MethodBinding$Builder$Build.apply(MethodDelegationBinder.java:507)
at net.bytebuddy.instrumentation.method.bytecode.stack.StackManipulation$Compound.apply(StackManipulation.java:194)
at net.bytebuddy.instrumentation.MethodDelegation$Appender.apply(MethodDelegation.java:998)
at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Entry$Simple.apply(TypeWriter.java:1055)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Engine$ForCreation.create(TypeWriter.java:688)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1143)
at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:219)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$AbstractDelegatingBuilder.make(DynamicType.java:1611)
at X.main(X.java:40)
When I change WrappedObject class to:
public static class WrappedObject {
Object method1() {...}
Object method2() {...}
}
it runs fine.
So, is @pipe capable of delegation to void returning methods? Or am I doing something wrong?
Byte Buddy does currently not weave bridge methods. Therefore, overriding methods with type variables in their signature are currently not overridden. Byte Buddy must discover such methods and add bridge methods on demand.
Similarly, when invoking a super method, the signature of the erased method must be resolved correctly.
I'd like to use Byte Buddy in an OSGi application but cannot because the jar does not contain the necessary META-INF/manifest headers.
The easy way to add them is to use the maven-bundle-plugin.
Hi,
The sample code snippet throws FileNotFoundException. If the folder structure created manually (c:/app-classpath/sample/app) saveIn works successfully. I am not sure if this is a bug, should i create package structure manually?
Thanks
package sample.app;
import java.io.File;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType.Unloaded;
import net.bytebuddy.instrumentation.FixedValue;
import net.bytebuddy.instrumentation.method.matcher.MethodMatchers;
public class Sample {
public String test() {
return null;
}
public static void main(String[] args) throws Exception {
File file = new File("C:\\app-classpath");
file.mkdir();
ByteBuddy buddy = new ByteBuddy();
Unloaded<Sample> unloaded = buddy
.subclass(Sample.class)
.method(MethodMatchers.named("test"))
.intercept(FixedValue.value("test"))
.make();
unloaded.saveIn(file);
}
}
Exception Message:
Exception in thread "main" java.io.FileNotFoundException: C:\app-classpath\sample\app\Sample$ByteBuddy$314824326.class (No such file or directory)
at java.io.FileOutputStream.open(Native Method)
at java.io.FileOutputStream.<init>(FileOutputStream.java:206)
at java.io.FileOutputStream.<init>(FileOutputStream.java:156)
at net.bytebuddy.dynamic.DynamicType$Default.saveIn(DynamicType.java:1289)
at sample.app.Sample.main(Sample.java:25)
Environment:
Windows 8.1
Java 1.8.0_20-b26
If your super type is an interface the use of the current out of the box naming strategies result in the name of the proxy class being the concatenation of javaLangPackagePrefix, the Object class name, and random string. While this is fine in terms of creating a unique proxy class names it leads to the loss of contextual information about what's being proxyed.
I'd like to propose the addition of a new naming strategy that uses the fully qualified name of the first available declared interface if the super type represents Object.class type. If the super type does not represent an Object.class type the naming strategy will be behaves like SuffixingRandom naming strategy. Something like this:
/**
* A naming strategy that uses the name of the first interface if the super type is {@link Object Object.class}
* otherwise the naming strategy behaves exactly like the {@link SuffixingRandom} naming strategy.
* <ol>
* <li>The fully qualified name of the super class or the first interface</li>
* <li>A given suffix string</li>
* <li>A random number</li>
* </ol>
* Between all these elements, a {@code $} sign is included into the name to improve readability. As an exception,
* types that subclass classes from the {@code java.**} packages are prefixed with a given package. This is
* necessary as it is illegal to define non-bootstrap classes in this name space. The same strategy is applied when
* subclassing a signed type which is equally illegal.
*/
static class FirstInterface implements NamingStrategy {
/**
* The package prefix of the {@code java.**} packages for which the definition of non-bootstrap types is
* illegal.
*/
private static final String JAVA_PACKAGE = "java.";
/**
* The default package for defining types that are renamed to not be contained in the
* {@link net.bytebuddy.NamingStrategy.SuffixingRandom#JAVA_PACKAGE} package.
*/
private static final String BYTE_BUDDY_RENAME_PACKAGE = "net.bytebuddy.renamed";
/**
* The suffix to attach to a super type name.
*/
private final String suffix;
/**
* The renaming location for types of the {@link net.bytebuddy.NamingStrategy.SuffixingRandom#JAVA_PACKAGE}.
*/
private final String javaLangPackagePrefix;
/**
* An instance for creating random seed values.
*/
private final RandomString randomString;
/**
* Creates an immutable naming strategy with a given suffix but moves types that subclass types within the
* {@code java.lang} package into ByteBuddy's package namespace.
*
* @param suffix The suffix for the generated class.
*/
public FirstInterface(String suffix) {
this(suffix, BYTE_BUDDY_RENAME_PACKAGE);
}
/**
* Creates an immutable naming strategy with a given suffix but moves types that subclass types within the
* {@code java.lang} package into a given namespace.
*
* @param suffix The suffix for the generated class.
* @param javaLangPackagePrefix The fallback namespace for type's that subclass types within the
* {@code java.lang} namespace.
*/
public FirstInterface(String suffix, String javaLangPackagePrefix) {
this.suffix = suffix;
this.javaLangPackagePrefix = javaLangPackagePrefix;
randomString = new RandomString();
}
@Override
public String name(NamingStrategy.UnnamedType unnamedType) {
TypeDescription superClass = unnamedType.getSuperClass();
Collection<TypeDescription> interfaces = unnamedType.getDeclaredInterfaces();
if (superClass.represents(Object.class) && !interfaces.isEmpty()) {
superClass = interfaces.iterator().next();
}
String superClassName = superClass.getName();
if (superClassName.startsWith(JAVA_PACKAGE) || superClass.isSealed()) {
superClassName = javaLangPackagePrefix + "." + superClassName;
}
return String.format("%s$%s$%s", superClassName, suffix, randomString.nextString());
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other == null || getClass() != other.getClass()) {
return false;
}
FirstInterface that = (FirstInterface) other;
return javaLangPackagePrefix.equals(that.javaLangPackagePrefix)
&& suffix.equals(that.suffix);
}
@Override
public int hashCode() {
int result = suffix.hashCode();
result = 31 * result + javaLangPackagePrefix.hashCode();
return result;
}
@Override
public String toString() {
return "NamingStrategy.FirstInterface{"
+ "suffix='" + suffix + '\''
+ ", javaLangPackagePrefix='" + javaLangPackagePrefix + '\''
+ ", randomString=" + randomString
+ '}';
}
}
Currently, Byte Buddy erases the types of two methods if they are defined by two incompatible interfaces. Alternatively, this should only be done if the generic types of the interfaces are in conflict.
Hi~
I want to use byte-buddy to generate a proxy-class at compile stage...
e.g. input file like
public class Rule {
@AnRule
public void doRule1() {
}
@AnRule
public void doRule2() {
}
}
and want to generate a proxy
public class RuleInvoker implement Invoker {
private Rule rule;
public RuleInvoker() {
this.rule = new Rule();
}
public void invoke() {
this.rule.doRule1();
this.rule.doRule2();
}
}
now, I can generate RuleInvoker
with rule
field, and I create a constructor..
But...how can I new a Rule
then set it to rule
field at constructor...
I try this..
final Constructor constructor = rawRuleClazz.getConstructor();
final DynamicType.Unloaded<Invoker> invokerType = new ByteBuddy()
.subclass(Invoker.class, ConstructorStrategy.Default.NO_CONSTRUCTORS)
.defineField("rule", new TypeDescription.ForLoadedType(rawRuleClazz),
Visibility.PRIVATE, FieldManifestation.FINAL)
.defineConstructor(Collections.<Class<?>>emptyList(), Visibility.PUBLIC)
.intercept(new Instrumentation() {
@Override
public InstrumentedType prepare(InstrumentedType instrumentedType) {
return instrumentedType;
}
@Override
public ByteCodeAppender appender(final Target instrumentationTarget) {
return new ByteCodeAppender() {
@Override
public boolean appendsCode() {
return true;
}
@Override
public Size apply(MethodVisitor methodVisitor,
Context instrumentationContext,
MethodDescription instrumentedMethod) {
StackManipulation.Size size = new StackManipulation.Compound(
MethodVariableAccess.REFERENCE.loadOffset(0),
MethodInvocation
.invoke(new TypeDescription.ForLoadedType(
TestRule.class)
.getDeclaredMethods()
.filter(isConstructor()
.and(takesArguments(0)))
.getOnly()),
MethodVariableAccess.REFERENCE.loadOffset(0),
MethodConstant.forMethod(
new MethodDescription.ForLoadedConstructor(
constructor)),
FieldAccess.forField(
instrumentationTarget.getTypeDescription()
.getDeclaredFields().get(0))
.putter(),
MethodReturn.VOID
).apply(methodVisitor, instrumentationContext);
return new Size(size.getMaximalSize(),
instrumentedMethod.getStackSize());
}
};
}
})
.make();
It seems that doest do invokespecial
, has any short-cut to do this?
^ ^ thx~
A visibility bridge method is currently identified by checking a bridge method for not being a bridge of a generic type. It could however also be a bridge for a return type override what must be checked.
Can you simplify the API so it is more intuitive like in https://github.com/square/javapoet
Using the code in the example:
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
ClassLoadingStrategy classLoadingStrategy = ClassReloadingStrategy.fromInstalledAgent();
class Foo {
String m() {
return "foo";
}
}
class Bar {
String m() {
return "bar";
}
}
Foo foo = new Foo();
new ByteBuddy().redefine(Bar.class).name(Foo.class.getName()).make().load(Foo.class.getClassLoader(), classLoadingStrategy);
System.out.println(foo.m());
Prints out "bar" as expected but throws an IllegalStateException right after.
java.lang.IllegalStateException: ClassReader.accept() should be called with EXPAND_FRAMES flag
at net.bytebuddy.jar.asm.commons.LocalVariablesSorter.visitFrame(Unknown Source) ~[byte-buddy-0.6.12.jar:?]
at net.bytebuddy.jar.asm.commons.RemappingMethodAdapter.visitFrame(Unknown Source) ~[byte-buddy-0.6.12.jar:?]
at net.bytebuddy.jar.asm.ClassReader.a(Unknown Source) ~[byte-buddy-0.6.12.jar:?]
at net.bytebuddy.jar.asm.ClassReader.b(Unknown Source) ~[byte-buddy-0.6.12.jar:?]
at net.bytebuddy.jar.asm.ClassReader.accept(Unknown Source) ~[byte-buddy-0.6.12.jar:?]
at net.bytebuddy.jar.asm.ClassReader.accept(Unknown Source) ~[byte-buddy-0.6.12.jar:?]
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining.doCreate(TypeWriter.java:1351) ~[byte-buddy-0.6.12.jar:?]
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining.create(TypeWriter.java:1335) ~[byte-buddy-0.6.12.jar:?]
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:917) ~[byte-buddy-0.6.12.jar:?]
at net.bytebuddy.dynamic.scaffold.inline.RedefinitionDynamicTypeBuilder.make(RedefinitionDynamicTypeBuilder.java:209) ~[byte-buddy-0.6.12.jar:?]
Today, only type descriptions and not generic type description are iterable over their super types.
Can you provide the functionality to in line methods though annotations?
Is it possible to extend support in Byte Buddy to manipulate source code / AST through something like Janino with similar interface?
new ByteBuddy().redefine(BlockDirt.class).method(named("updateTick")).intercept(MethodDelegation.to(CppDirtInterceptor.class)).make().load(BlockDirt.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
Upon calling the load method this crashes, despite the CppDirtInterceptor containing a method taking the exact parameters that the updateTick method in the class takes.
java.lang.UnsupportedOperationException: class redefinition failed: attempted to add a method
at sun.instrument.InstrumentationImpl.redefineClasses0(Native Method) ~[?:1.8.0_40]
at sun.instrument.InstrumentationImpl.redefineClasses(InstrumentationImpl.java:170) ~[?:1.8.0_40]
at net.bytebuddy.dynamic.loading.ClassReloadingStrategy$Engine$1.apply(ClassReloadingStrategy.java:242) ~[byte-buddy-0.6.12.jar:?]
at net.bytebuddy.dynamic.loading.ClassReloadingStrategy.load(ClassReloadingStrategy.java:154) ~[byte-buddy-0.6.12.jar:?]
at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:3486) ~[byte-buddy-0.6.12.jar:?]
Note that the method is not declared directly in the BlockDirt class, but rather it is declared in the Block class it extends from. Could that be why?
Add functionality to flatten and object so it in a more optimised layout.
Add a builder for defining annotation descriptions programmatically in order to allow easily defining values of unloaded annotations. This is useful when defining annotated types and when definign default values for annotations.
Also, from this builder, allow to define EnumerationValue
s directly for using with default values.
Ability to unroll short loops.
after addressing #55 i propose to reword API, doc and exceptions.
On Java 9, tools jar is not required. it is part of the appmodules.jimage.
I am getting a complete class definition from another application in the form of a string, as well as some information about it like the class and package name. How can I pass use this information to create and load the class using the Byte Buddy?
PS: The classes have to implement an interface which my application knows about.
Given the following interface I would like to be able to generate a bean with minimal effort using ByeBuddy. Ideally, I would like to simply give ByteBuddy an interface with getter and setter pairs and have it take care of doing the analysis on the interface and create a dynamic java bean.
public interface UserConfig {
String getName();
void setName(String name);
int getAge();
void setAge(int age);
}
In playing with ByteBuddy on some things that currently use Cglib, I noticed that there's no way out of the box to have constructors created for a package private superclass/constructor. For even a basic package private type like class Foo{}
, with ConstructorStrategy.Default.IMITATE_SUPER_TYPE
a ByteBuddy generated subclass contains no constructors, which prevents me from using it via reflection. With ConstructorStrategy.Default.DEFAULT_CONSTRUCTOR
I get an IllegalArgumentException
on the subclass
call.
Can ConstructorStrategy.Default be enhanced to support package private and private classes/constructors? Is there a current workaround to get constructors imitating the super type generated for package private and private classes?
This is a followup of mockito issues mockito/mockito#233 and PR mockito/mockito#234
The fix 6f223d9 here don't handle compilation, so bytecode is can be generated, but cannot be instantiated (IllegalAccessError
).
The issue is probably there MethodRegistry.Default.Compiled#getInstrumentedMethods
does not check if methods are visible, like other components of 6f223d9.
I'm guessing that the implementation should be something like :
@Override
public MethodList getInstrumentedMethods() {
return new MethodList.Explicit(new ArrayList<MethodDescription>(implementations.keySet())).filter(not(isTypeInitializer()).and(isVisibleTo(instrumentedType)));
}
Hi,
imho it is more convenient if the latest note are at the top and the oldest are at the button.
Thanks for providing byte-buddy!
Cheers,
Pascal
Using Byte Buddy and PowerMock throws a ClassNotFoundException
I uploaded the test case and the stacktrace on my gist https://gist.github.com/juherr/69b3f7604f03c1946207
I suppose the problem is liked to the MockClassLoader
from PowerMock but I've no idea how to fix it.
When transforming an instrumented method, this method is recreated as a latent method description and not retained in its declared form. This breaks the lookup of, for example, method constants. Therefore, a more flexible transformer is required.
I am trying to create a proxy class that delegates all method invocation to an existing InvocationHandler implementation without success. The issue seems to stem from having a setter method that has a primitive parameter in my implemented interface (ProxyedInterface). With the bellow test case I get the following exception:
java.lang.VerifyError: Bad type on operand stack
Exception Details:
Location:
test/ProxyedInterface_Proxy_T3rJCc0Y.setPrimitive(JLjava/lang/String;)V @33: aastore
Reason:
Type long_2nd (current frame, stack[7]) is not assignable to 'java/lang/Object'
Current Frame:
bci: @33
flags: { }
locals: { 'test/ProxyedInterface_Proxy_T3rJCc0Y', long, long_2nd, 'java/lang/String' }
stack: { 'java/lang/reflect/InvocationHandler', 'test/ProxyedInterface_Proxy_T3rJCc0Y', 'java/lang/reflect/Method', '[Ljava/lang/Object;', '[Ljava/lang/Object;', integer, long, long_2nd }
Bytecode:
0x0000000: b200 102a 1206 1222 05bd 0013 5903 b200
0x0000010: 2653 5904 1229 53b6 0019 05bd 0004 5903
0x0000020: 1f53 5904 2d53 b900 1f04 0057 b1
at java.lang.Class.getDeclaredFields0(Native Method)
at java.lang.Class.privateGetDeclaredFields(Class.java:2575)
at java.lang.Class.getDeclaredField(Class.java:2060)
at net.bytebuddy.instrumentation.LoadedTypeInitializer$ForStaticField.onLoad(LoadedTypeInitializer.java:119)
at net.bytebuddy.instrumentation.LoadedTypeInitializer$Compound.onLoad(LoadedTypeInitializer.java:195)
at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.initialize(DynamicType.java:2519)
at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:2507)
at saden1.issues.bytebuddy.ServiceLocatorProxyNGTest.testSomeMethod(ServiceLocatorProxyNGTest.java:37)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.ClassFileVersion;
import net.bytebuddy.NamingStrategy;
import net.bytebuddy.dynamic.ClassLoadingStrategy;
import net.bytebuddy.instrumentation.InvocationHandlerAdapter;
import static net.bytebuddy.instrumentation.method.matcher.MethodMatchers.*;
import net.bytebuddy.utility.RandomString;
import org.testng.annotations.Test;
public class ProxyNGTest {
@Test
public void testSomeMethod() throws InstantiationException, IllegalAccessException {
Class<?> type = ProxyedInterface.class;
ClassLoader classLoader = type.getClassLoader();
Class<?> proxyType = new ByteBuddy(ClassFileVersion.JAVA_V8)
.withNamingStrategy(new ProxyNamingStrategy(type))
.subclass(Object.class)
.implement(type)
.method(isDeclaredBy(type))
.intercept(InvocationHandlerAdapter.of(new ProxyInvocationHandler()))
.make()
.load(classLoader, ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
Object result = proxyType.newInstance();
}
public static interface ProxyedInterface {
//fails on any setter that takes a primitive
void setPrimitive(long value);
//fails on any setter that takes a primitive
void setPrimitive(long value, String string);
//works
void setObject(Long value);
}
public static class ProxyInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(proxy, args);
}
}
public static class ProxyNamingStrategy implements NamingStrategy {
private final Class<?> type;
public ProxyNamingStrategy(Class<?> type) {
this.type = type;
}
@Override
public String name(UnnamedType unnamedType) {
String packageName = type.getPackage().getName();
String simpleName = type.getSimpleName();
return String.format("test.%s_Proxy_%s", simpleName, RandomString.make());
}
}
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.