1. Overview <br />Agent is a design pattern, whose purpose is to provide another object with a proxy to control access to a certain object. The proxy class is responsible for preprocessing messages for the delegate class, filtering messages and forwarding messages, and performing subsequent processing after the message is executed by the delegate class. To maintain consistency in behavior, proxy classes and delegate classes usually implement the same interface.
According to the period of agent creation, agent classes can be divided into two types:
Static proxy: The programmer creates a proxy class or a specific tool to automatically generate source code and then compile it. That is to say, the .class file of the proxy class already exists before the program runs.
Dynamic proxy: Use reflection mechanism to create and generate dynamically when the program is running.
Let’s briefly introduce the static proxy before implementing the dynamic proxy mechanism.
2. Static proxy <br />As mentioned above, both proxy classes and delegate classes generally need to implement the same interface. The following is to define this interface first:
public interface Service{ public void add();}The delegate class is an implementation of an interface, defined as follows:
public class ServiceImpl implements Service{ public void add() { System.out.println("Add user!"); }}If we want to add some logs to the delegate class, the proxy class can be defined as follows:
public class ServiceProxy implements Service{ private Service service; public ServiceProxy(Service service) { super(); this.service = service; } public void add() { System.out.println("Service start"); service.add(); System.out.println("Service end"); }}Write test classes:
public class TestMain{ public static void main(String[] args) { Service serviceImpl=new ServiceImpl(); Service proxy=new ServiceProxy(serviceImpl); proxy.add(); }}Run the test program, the results are as follows:
From the above code, we can see that the static proxy class can only serve a specific interface. If you want to serve multiple types of objects, you must proxy each object. We will think about whether all the proxy functions can be completed through a proxy class, so we introduced the concept of dynamic proxy.
3. Dynamic Proxy Java's dynamic proxy mainly involves two classes, Proxy and InvocationHandler.
Proxy: Provides a set of static methods to dynamically generate proxy classes and their objects for a set of interfaces.
// Method 1: This method is used to obtain the call processor associated with the specified proxy object. static InvocationHandler getInvocationHandler(Object proxy)// Method 2: This method is used to obtain the class object of the dynamic proxy class associated with the specified class loader and a set of interfaces. static Class getProxyClass(ClassLoader loader, Class[] interfaces)// Method 3: This method is used to determine whether the specified class object is a dynamic proxy class static boolean isProxyClass(Class cl)// Method 4: This method is used to generate dynamic proxy class instances for the specified class loader, a set of interfaces and a set of interfaces. static Object newProxyInstance(ClassLoader loader, Class[] interfaces,InvocationHandler h)
InvocationHandler: It is a calling processor interface, customizes an invok method, which is used to centrally handle method calls on dynamic proxy class objects, usually in which proxy access to delegate classes is implemented
// This method is responsible for centrally handling all method calls on the dynamic proxy class. The first parameter is both an instance of the proxy class, and the second parameter is the method object being called // The third method is the call parameter. The call processor preprocesses or dispatches to the delegate class instance to transmit execution Object invoke(Object proxy, Method method, Object[] args)
To implement dynamic proxy for Java, there are four specific steps:
1. Create your own call processor by implementing the InvocationHandler interface
2. Create a dynamic proxy class by specifying ClassLoader object and a set of interfaces for the Proxy class
3. Obtain the constructor of the dynamic proxy class through the reflection mechanism, and its only parameter type is the call processor class interface type
4. Create a dynamic proxy class instance through the constructor. During construction, the processor object is called as a parameter and is passed in.
The following is an example of implementing your own dynamic proxy based on the above four steps:
The implementation class of interface and interface (i.e. delegate class) is the same as the code of the above static proxy. Here we will implement the InvocationHandler interface to create our own call processor.
public class ServiceHandle implements InvocationHandler{ private Object s; public ServiceHandle(Object s) { this.s = s; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Service start"); //invoke means calling the underlying method represented by this Method object on a specified object with specified parameters Object result=method.invoke(s, args); System.out.println("Service end"); return result; }}Write test classes:
public class TestMain{ public static void main(String[] args) { Service service=new ServiceImpl(); InvocationHandler handler=new ServiceHandle(service); Service s=(Service) Proxy.newProxyInstance(service.getClass().getClassLoader(), service.getClass().getInterfaces(), handler); s.add(); }}Run the test program, and the result is the same as the static proxy. We can see that the above code does not have steps 2 and 3 we mentioned before, because Prox's static method newProxyInstance has encapsulated these two steps for us. The specific internal implementation is as follows:
// Dynamically create a class object of the proxy class for a set of interfaces including the Interface interface through Proxy Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });// Obtain the constructor object from the generated class object through reflection Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });// Create a dynamic proxy class instance through the constructor object Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });The internal implementation of the newProxyInstance function is:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException { //Check h is not empty, otherwise throw exception Objects.requireNonNull(h); //Get the proxy class type object related to formulating the class loader and a set of interfaces final Class<?>[] intfs = interfaces.clone(); //Check whether the interface class object is visible to the class loader and is exactly the same as the interface class object recognized by the class loader final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } //Get the proxy class type object related to formulating the class loader and a set of interfaces Class<?> cl = getProxyClass0(loader, intfs); try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } //Get the constructor object through reflection and generate a proxy class instance final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } } 4. Simulate and implement Proxy class
According to the above principle introduction, we can simulate and implement the Proxy class by ourselves:
public class Proxy{ public static Object newProxyInstance(Class inface,InvocationHandle h) throws Exception { String rt="/r/n"; String methodStr=""; Method[] methods=inface.getMethods(); for(Method m:methods) { methodStr+="@Override"+rt+ "public void "+m.getName()+"()"+rt+"{" + rt + "try {"+rt+ " Method md="+inface.getName()+".class.getMethod(/""+m.getName()+"/");"+rt+ "h.invoke(this,md);"+rt+ " } catch(Exception e){e.printStackTrace();}"+rt+ "}"; } String src="package test;"+rt+ "import java.lang.reflect.Method;"+rt+ "public class ServiceImpl2 implements "+inface.getName()+ rt+ "{"+rt+ "public ServiceImpl2(InvocationHandle h)"+rt+ "{"+rt+ "this.h = h;"+rt+ "}"+rt+ " test.InvocationHandle h;"+rt+ methodStr+ "}"; String fileName="d:/src/test/ServiceImpl2.java"; //compile compile(src, fileName); //load into memory and create instance Object m = loadMemory(h); return m; } private static void compile(String src, String fileName) throws IOException { File f=new File(fileName); FileWriter fileWriter=new FileWriter(f); fileWriter.write(src); fileWriter.flush(); fileWriter.close(); //Get the Java compiler provided by this platform, JavaCompiler compiler=ToolProvider.getSystemJavaCompiler(); //Get a new instance implemented by a standard file manager StandardJavaFileManager fileManager=compiler.getStandardFileManager(null,null, null); //Get the file object representing the given file Iterable units=fileManager.getJavaFileObjects(fileName); //Create future CompilationTask t=compiler.getTask(null, fileManager, null, null, null, units); //Execute this compilation task t.call(); fileManager.close(); } private static Object loadMemory(InvocationHandle h) throws MalformedURLException, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { URL[] urls=new URL[] {new URL("file:/"+"d:/src/")}; //Load class and resource URLClassLoader ul=new URLClassLoader(urls); Class c=ul.loadClass("test.ServiceImpl2"); //Returns the specified public constructor of the class represented by the Class object. Constructor ctr=c.getConstructor(InvocationHandle.class); //Use the constructor method represented by this Constructor object ctr to create a new instance of the declaration class of the constructor method, and initialize the instance with the specified initialization parameter Object m = ctr.newInstance(h); return m; }}5. Summary 1. The so-called dynamic proxy is such a class. It is a class generated at runtime. When generating it, you must provide a set of interfaces to it, and then change the class to claim that it implements these interfaces. However, it will not do substantial work for you, but will take over the actual work based on the parameter handler (that is, the implementation class of the InvocationHandler interface) provided when you generate the instance.
2. Proxy's design makes it only support interface proxy. Java's inheritance mechanism destined that the dynamic proxy class cannot implement dynamic proxy for class, because multiple inheritance is essentially not feasible in Java.
The above is all about this article, I hope it will be helpful to everyone's learning.