With the emergence of Java dynamic proxy mechanism, Java developers do not need to write proxy classes manually. As long as they simply specify a set of interfaces and delegate class objects, they can dynamically obtain proxy classes. The proxy class is responsible for dispatching all method calls to the delegate object for reflection and execution. In the process of dispatching and execution, developers can also adjust the delegate object and its functions as needed. This is a very flexible and flexible proxy framework. Now let's start the study of dynamic agent.
Brief description of dynamic agent
In the dynamic proxy mechanism of java, there are two important classes or interfaces, one is InvocationHandler(Interface) and the other is Proxy(Class).
1, Description of InvocationHandler(interface):
InvocationHandler is the interface implemented by the invocation handler of a proxy instance. Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.
Every dynamic proxy class must implement the InvocationHandler interface, and the instance of each proxy class is associated with a handler. When we call a method through the proxy object, the call of this method will be forwarded to the invoke method of the InvocationHandler interface. Let's take a look at the only method of the InvocationHandler interface: invoke method:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
This method receives three parameters and returns an Object type. Their respective meanings are as follows:
- proxy: refers to the real object we represent
- Method: refers to the method object that we want to call the method of the real object
- args: refers to the parameters accepted when calling a method of a real object
The returned Object refers to the return type of the real Object method, which will be further understood in the following examples.
the value to return from the method invocation on the proxy instance.
2, Proxy(Class) Description:
Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.
Proxy class is used to dynamically create a proxy object. We often use the newProxyInstance method:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
Understanding of parameters:
//A ClassLoader object that defines which ClassLoader object loads the generated proxy object loader - the class loader to define the proxy class //An array of Interface objects, which indicates what interfaces I will provide to the objects I need to proxy interfaces - the list of interfaces for the proxy class to implement //An InvocationHandler object indicates which InvocationHandler object will be associated when I call the method as a dynamic proxy object h - the invocation handler to dispatch method invocations to
Understanding of the returned result: an instance of a proxy object
a proxy instance with the specified invocation handler of a proxy class that is defined by the specified class loader and that implements the specified interfaces
Simple Java proxy
We create a Java project for testing and understanding dynamic agents. The project structure is as follows:
1, First define an Interface and add two methods.
package com.huhx.proxy; public interface Interface { void getMyName(); String getNameById(String id); }
2, Define a real class that implements the above interface, RealObject:
package com.huhx.proxy; public class RealObject implements Interface { @Override public void getMyName() { System.out.println("my name is huhx"); } @Override public String getNameById(String id) { System.out.println("argument id: " + id); return "huhx"; } }
3, Defining a proxy object also implements the above Interface:
package com.huhx.proxy; public class SimpleProxy implements Interface { private Interface proxied; public SimpleProxy(Interface proxied) { this.proxied = proxied; } @Override public void getMyName() { System.out.println("proxy getmyname"); proxied.getMyName(); } @Override public String getNameById(String id) { System.out.println("proxy getnamebyid"); return proxied.getNameById(id); } }
4, SimpleMain in the Main method, test the above results:
package com.huhx.proxy; public class SimpleMain { private static void consume(Interface iface) { iface.getMyName(); String name = iface.getNameById("1"); System.out.println("name: " + name); } public static void main(String[] args) { consume(new RealObject()); System.out.println("========================================================"); consume(new SimpleProxy(new RealObject())); } }
5, The results of the operation are as follows:
my name is huhx argument id: 1 name: huhx ======================================================== proxy getmyname my name is huhx proxy getnamebyid argument id: 1 name: huhx
Dynamic proxy for Java
After completing the above simple java proxy, now we begin to learn the dynamic proxy of Java, which is a step forward than the idea of proxy, because it can dynamically create the proxy and dynamically process the calls to the proxy methods. All calls made on the dynamic agent will be redirected to a single call processor. Its job is to reveal the type of call and determine the corresponding countermeasures. Let's deepen the understanding of Java dynamic proxy through a case:
1, Create a processor that inherits InvocationHandler: DynamicProxyHandler
package com.huhx.dynamicproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Arrays; public class DynamicProxyHandler implements InvocationHandler { private Object proxied; public DynamicProxyHandler(Object proxied) { System.out.println("dynamic proxy handler constuctor: " + proxied.getClass()); this.proxied = proxied; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("dynamic proxy name: " + proxy.getClass()); System.out.println("method: " + method.getName()); System.out.println("args: " + Arrays.toString(args)); Object invokeObject = method.invoke(proxied, args); if (invokeObject != null) { System.out.println("invoke object: " + invokeObject.getClass()); } else { System.out.println("invoke object is null"); } return invokeObject; } }
2. We write a Main method for testing, DynamicProxyMain:
package com.huhx.dynamicproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import com.huhx.proxy.Interface; import com.huhx.proxy.RealObject; public class DynamicProxyMain { public static void consumer(Interface iface) { iface.getMyName(); String name = iface.getNameById("1"); System.out.println("name: " + name); } public static void main(String[] args) throws Exception, SecurityException, Throwable { RealObject realObject = new RealObject(); consumer(realObject); System.out.println("=============================="); // Dynamic agent ClassLoader classLoader = Interface.class.getClassLoader(); Class<?>[] interfaces = new Class[] { Interface.class }; InvocationHandler handler = new DynamicProxyHandler(realObject); Interface proxy = (Interface) Proxy.newProxyInstance(classLoader, interfaces, handler); System.out.println("in dynamicproxyMain proxy: " + proxy.getClass()); consumer(proxy); } }
3, The operation results are as follows:
my name is huhx argument id: 1 name: huhx ============================== dynamic proxy handler constuctor: class com.huhx.proxy.RealObject in dynamicproxyMain proxy: class com.sun.proxy.$Proxy0 dynamic proxy name: class com.sun.proxy.$Proxy0 method: getMyName args: null my name is huhx invoke object is null dynamic proxy name: class com.sun.proxy.$Proxy0 method: getNameById args: [1] argument id: 1 invoke object: class java.lang.String name: huhx
From the above output results, we can draw the following conclusions:
- The InvocationHandler associated with the proxy object will execute its invoke method only when the proxy object calls the method
- invoke's understanding of the three parameters: Object proxy is the object of the proxy, Method method is the Method class of the calling method in the real object, and Object[] args is the parameter of the calling method in the real object.
Principles of Java Dynamic agent
1, The key code of dynamic proxy is proxy Newproxyinstance (classloader, interfaces, handler). Let's follow up the source code to see:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { // handler cannot be empty if (h == null) { throw new NullPointerException(); } final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. */ // Get the Class object of the proxy through the loader and interface Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) { // create proxy instance with doPrivilege as the proxy class may // implement non-public interfaces that requires a special permission return AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { return newInstance(cons, ih); } }); } else { // Create an instance of the proxy object return newInstance(cons, ih); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } }
2, Let's take a look at the source code of the newInstance method:
private static Object newInstance(Constructor<?> cons, InvocationHandler h) { try { return cons.newInstance(new Object[] {h} ); } catch (IllegalAccessException | InstantiationException e) { throw new InternalError(e.toString()); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString()); } } }
3, When we call a method through a proxy object, the call of this method will be forwarded to the invoke method of the InvocationHandler interface.
I didn't find the code reflecting this sentence in the source code, so I added the following code to the main method of the test class:
if (proxy instanceof Proxy) { InvocationHandler invocationHandler = Proxy.getInvocationHandler(proxy); invocationHandler.invoke(proxy, realObject.getClass().getMethod("getMyName"), null); System.out.println("--------------------------------------"); }
The output of this code is the same as the output of the getMyName method in the proxy object mentioned above. I don't know if the Jvm bottom is so judged.
dynamic proxy handler constuctor: class com.huhx.proxy.RealObject dynamic proxy name: class com.sun.proxy.$Proxy0 method: getMyName args: null my name is huhx invoke object is null --------------------------------------
Don't lose heart if you don't understand it for a while. After all, it took me a lot of time to understand it. If you don't understand, you can read it several times, or you can add Xiaobian's V:K15197783918 to communicate with Xiaobian.