Forked from thomasdarimont/DefaultMethodProxyExample.java
Last active
August 29, 2015 14:15
-
-
Save danieldietrich/a49e83668ccfa6ca2d7f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package labs; | |
import java.lang.invoke.MethodHandles; | |
import java.lang.reflect.InvocationHandler; | |
import java.lang.reflect.Method; | |
import java.lang.reflect.Proxy; | |
import java.util.Arrays; | |
import java.util.concurrent.ConcurrentHashMap; | |
import java.util.concurrent.ConcurrentMap; | |
import java.util.stream.Collectors; | |
import jdk.internal.org.objectweb.asm.ClassWriter; | |
import jdk.internal.org.objectweb.asm.MethodVisitor; | |
import jdk.internal.org.objectweb.asm.Opcodes; | |
import jdk.internal.org.objectweb.asm.Type; | |
/** | |
* @author Thomas Darimont | |
*/ | |
public class DefaultMethodProxyExample { | |
public static void main(String[] args) throws Throwable { | |
MessageHandler staticHandler = createInstanceViaAnonymousInnerClass(); | |
staticHandler.handle(new Message()); | |
MessageHandler dynamicProxyHandler = createDynamicInstanceViaDynamicProxy(); | |
dynamicProxyHandler.handle(new Message()); | |
MessageHandler dynamicGeneratedHandler = createDynamicInstanceViaGeneratedClass(); | |
dynamicGeneratedHandler.handle(new Message()); | |
} | |
private static MessageHandler createDynamicInstanceViaDynamicProxy() { | |
ClassLoader cl = Thread.currentThread().getContextClassLoader(); | |
Class<?>[] ifaces = new Class[] { MessageHandler.class }; | |
InvocationHandler ivocationHandler = (Object proxy, Method method, Object[] arguments) -> // | |
MethodHandles.lookup() // | |
.in(method.getDeclaringClass()) // | |
.unreflectSpecial(method, method.getDeclaringClass()) // | |
.bindTo(proxy) // | |
.invokeWithArguments(arguments); | |
return (MessageHandler) Proxy.newProxyInstance(cl, ifaces, ivocationHandler); | |
} | |
private static MessageHandler createDynamicInstanceViaGeneratedClass() { | |
return ProxyFactory.INSTANCE.newProxyInstance(MessageHandler.class); | |
} | |
private static MessageHandler createInstanceViaAnonymousInnerClass() { | |
return new MessageHandler() {}; | |
} | |
static class Message {} | |
public static interface MessageHandler { | |
default void handle(Message message) { | |
System.out.println("Demo: " + this.getClass()); | |
System.out.printf("Handle: %s%n", message); | |
System.out.printf("Callstack: %n%s%n %n", // | |
Arrays.asList(Thread.currentThread().getStackTrace()).stream() // | |
.map(Object::toString).collect(Collectors.joining("\n")) // | |
); | |
} | |
} | |
static enum ProxyFactory { | |
INSTANCE; | |
public <T> T newProxyInstance(Class<T> iface) { | |
Class<T> proxyClass = ProxyClassGenerator.INSTANCE.generateClass(iface); | |
try { | |
return iface.cast(proxyClass.newInstance()); | |
} catch (InstantiationException | IllegalAccessException e) { | |
throw new RuntimeException(e); | |
} | |
} | |
} | |
static enum ProxyClassGenerator implements Opcodes { | |
INSTANCE; | |
private static final String PROXY_SUFFIX = "_Proxy"; | |
private static final ConcurrentMap<Class<?>, Class<?>> PROXY_CLASS_CACHE = new ConcurrentHashMap<>(); | |
private Class<?> generateProxyClass(Class<?> iface) { | |
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); | |
String ifaceTypeName = Type.getInternalName(iface); | |
String proxyClassName = ifaceTypeName + PROXY_SUFFIX; | |
// class definition | |
cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, proxyClassName, null, "java/lang/Object", new String[] { ifaceTypeName }); | |
// default constructor | |
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); | |
mv.visitCode(); | |
mv.visitVarInsn(ALOAD, 0); | |
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); | |
mv.visitInsn(RETURN); | |
mv.visitMaxs(0, 0); | |
mv.visitEnd(); | |
return ByteArrayClassLoader.INSTANCE.loadClass(iface.getName() + PROXY_SUFFIX, cw.toByteArray()); | |
} | |
@SuppressWarnings("unchecked") | |
public <T> Class<T> generateClass(Class<T> iface) { | |
return (Class<T>) PROXY_CLASS_CACHE.computeIfAbsent(iface, this::generateProxyClass); | |
} | |
} | |
static class ByteArrayClassLoader extends ClassLoader { | |
public static final ByteArrayClassLoader INSTANCE = new ByteArrayClassLoader(); | |
private ByteArrayClassLoader() {} | |
public Class<?> loadClass(String name, byte[] bytes) { | |
Class<?> clazz = findLoadedClass(name); | |
return clazz != null ? clazz : defineClass(name, bytes, 0, bytes.length); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Demo: class labs.DefaultMethodProxyExample$1 | |
Handle: labs.DefaultMethodProxyExample$Message@3b07d329 | |
Callstack: | |
java.lang.Thread.getStackTrace(Thread.java:1552) | |
labs.DefaultMethodProxyExample$MessageHandler.handle(DefaultMethodProxyExample.java:66) | |
labs.DefaultMethodProxyExample.main(DefaultMethodProxyExample.java:25) | |
Demo: class com.sun.proxy.$Proxy0 | |
Handle: labs.DefaultMethodProxyExample$Message@70177ecd | |
Callstack: | |
java.lang.Thread.getStackTrace(Thread.java:1552) | |
labs.DefaultMethodProxyExample$MessageHandler.handle(DefaultMethodProxyExample.java:66) | |
java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:664) | |
java.lang.invoke.LambdaForm.interpret_L(LambdaForm.java:664) | |
java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:625) | |
labs.DefaultMethodProxyExample.lambda$0(DefaultMethodProxyExample.java:44) | |
labs.DefaultMethodProxyExample$$Lambda$6/1922154895.invoke(Unknown Source) | |
com.sun.proxy.$Proxy0.handle(Unknown Source) | |
labs.DefaultMethodProxyExample.main(DefaultMethodProxyExample.java:28) | |
Demo: class labs.DefaultMethodProxyExample$MessageHandler_Proxy | |
Handle: labs.DefaultMethodProxyExample$Message@6f539caf | |
Callstack: | |
java.lang.Thread.getStackTrace(Thread.java:1552) | |
labs.DefaultMethodProxyExample$MessageHandler.handle(DefaultMethodProxyExample.java:66) | |
labs.DefaultMethodProxyExample.main(DefaultMethodProxyExample.java:31) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment