Introduction to Dynamic Agent
- Originally a direct call to the function of the proxy object, but now it indirectly calls the function of the proxy object through a proxy object. When indirectly invoked, we can increase the function we want to achieve (referring to the implementation of invoke method of InvocationHandler subclass object, we can add the code we want to execute at will).
- Proxy objects and proxy objects look the same, because they all implement the same interface. This can be seen by decompiling bytecodes.
- In general, the java file is compiled into a class file first, and then the class loader loads the class bytecode file so that the JVM can generate the class object. But the dynamic proxy is to generate these bytecodes in memory and then load the bytecodes normally to generate objects, but you can't feel the existence of this class from beginning to end.
- Dynamic agents can only be targeted at interface s.
Sample code
There is an interface coder, which has two functions code and debug. The javaCoder class implements the interface coder.
import java.lang.reflect.*; interface coder{ void code(); void debug(String whichBug); } class javaCoder implements coder{ public void code(){ System.out.println("im coding java"); } public void debug(String whichBug){ System.out.println("im debuging java"+whichBug); } } class DynamicProxyHandler implements InvocationHandler { private Object proxied; public DynamicProxyHandler(Object proxied) { this.proxied = proxied; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("**** proxy: " + proxy.getClass() + ", method: " + method + ", args: " + args); if(args != null) for(Object arg : args) System.out.println(" " + arg); return method.invoke(proxied, args); } } class SimpleDynamicProxy { public static void consumer(coder who) { who.code(); who.debug("Null pointer exception"); System.out.println(who.toString()); System.out.println(who.hashCode()); System.out.println(who.equals(new Object())); } public static void main(String[] args) { System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); javaCoder javaer = new javaCoder(); consumer(javaer); System.out.println(); // Insert a proxy and call again: coder proxy = (coder)Proxy.newProxyInstance( coder.class.getClassLoader(), new Class[]{ coder.class }, new DynamicProxyHandler(javaer)); consumer(proxy); } }/*output: im coding java im debuging java Null pointer exception javaCoder@45ee12a7 1173230247 false **** proxy: class $Proxy0, method: public abstract void coder.code(), args: null im coding java **** proxy: class $Proxy0, method: public abstract void coder.debug(java.lang.String), args: [Ljava.lang.Object;@266474c2 Null pointer exception im debuging java Null pointer exception **** proxy: class $Proxy0, method: public java.lang.String java.lang.Object.toString(), args: null javaCoder@45ee12a7 **** proxy: class $Proxy0, method: public native int java.lang.Object.hashCode(), args: null 1173230247 **** proxy: class $Proxy0, method: public boolean java.lang.Object.equals(java.lang.Object), args: [Ljava.lang.Object;@6f94fa3e java.lang.Object@5e481248 false */
From the usage point of view, invoke method of InvocationHandler subclass object is the last process of dynamic proxy calling function. This is where we can freely design, for example, we can get the method proxy wants to call according to the name of Method object. We just pass the InvocationHandler subclass object to Proxy.newProxyInstance as the third parameter.
Automatically generated $Proxy0 source code
Before calling Proxy. new Proxy Instance, add a sentence System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); it will generate a $Proxy 0.class in the root directory of the project, which is not directly viewable by bytecode files, but IDEA will automatically decompile the class file for us.
Here is the $Proxy0 source code:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; final class $Proxy0 extends Proxy implements coder { private static Method m1; private static Method m4; private static Method m2; private static Method m3; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final void code() throws { try { super.h.invoke(this, m4, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void debug(String var1) throws { try { super.h.invoke(this, m3, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m4 = Class.forName("coder").getMethod("code"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("coder").getMethod("debug", Class.forName("java.lang.String")); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
- As can be seen from the class definition final class $Proxy0 extends Proxy implements coder, the dynamic proxy of java can only proxy the interface inte. Because it has inherited the Proxy class, the variable part is only implements coder. If two interfaces are proxied at the same time, then implements will follow two.
- From such static variables and static initialization blocks, it can be seen that they preserve the Method of coder interface and the Method object corresponding to the three methods of Object. By the way, getMethod sometimes has a second parameter because the first parameter corresponds to the Method's tangible parameter.
- The InvocationHandler subclass object passes through super(var1) in the constructor to the member variable H of the parent class, which executes super.h.invoke() every time a real call is needed.