preface
Proxy Pattern is one of the 23 commonly used design patterns of object-oriented software. Its function is to provide a proxy for other objects to control the access to this object, that is, middleman or purchasing agent
For example, the proxy class can be used to implement the function of the request directly after calling the proxy class. For example, the proxy class can be used to implement the function of the request directly after calling the proxy class.
Why add a layer of proxy classes? There are two advantages:
-
Hide and protect interface implementation classes and control direct access to interface implementation class objects
-
New functions can be added to improve the scalability of the system
JDK dynamic proxy is one of the commonly used proxy mechanisms, which is mainly realized through reflection. To realize JDK dynamic proxy, the InvocationHandler interface must be implemented. At the same time, it will be required to rewrite the invoke() method, as shown in the parameter list below
-
Proxy: dynamic proxy class (the person who buys it for you)
-
Method: called method
-
args: parameter of the called method
public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
practice
Steps for using JDK dynamic proxy
- Create the proxy interface and class;
- Create the implementation class of InvocationHandler interface and implement the proxy logic in the invoke method;
- Create a Proxy object through the Proxy's static method newproxyinstance (classloader, class [] interfaces, invocationhandler h)
- Use proxy objects.
Let's write a chestnut to show the calling process of dynamic proxy
The first is the interface of dynamic agent, which writes a method and overloads it
public interface TicketProvider { public void getTrainTicket(); public void getTrainTicket(int count); }
Then its implementation class
public class TicketProviderImpl implements TicketProvider { @Override public void getTrainTicket() { System.out.println("Buy train tickets"); } @Override public void getTrainTicket(int count) { System.out.println("Buy train tickets"+count+"Zhang"); } }
Then there is the proxy class, which implements the InvocationHandler interface and rewrites the invoke method
public class TicketProviderProxy implements InvocationHandler { //The actual object, that is, the store to which the purchasing agent is going private Object target; //Constructor assignment public TicketProviderProxy(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("-----before in method invoke-----"); //The corresponding method is found through the object and invoke() method.invoke(target, args); System.out.println("Add functionality to the method"); System.out.println("-----after in method invoke-----"); return null; } }
Let's test it
public class ProxyTest { public static void main(String[] args) { //Interface implementation class TicketProvider ticketProvider = new TicketProviderImpl(); //InvocationHandler implementation class uses the constructor just defined InvocationHandler handler = new TicketProviderProxy(ticketProvider); //newProxyInstance() generates a dynamic proxy class instance. args[0] is the implementation class and args[1] is the interface class to be, //args[2] is the InvocationHandler implementation class, that is, the processor, which processes the incoming real object that needs to be proxied TicketProvider ticketProviderProxy = (TicketProvider) Proxy.newProxyInstance( ticketProvider.getClass().getClassLoader(), ticketProvider.getClass().getInterfaces(), handler); //Incoming parameter test ticketProviderProxy.getTrainTicket(); ticketProviderProxy.getTrainTicket(5); } }
You can see that the output is
It can be seen that the proxy object created through the newProxyInstance() method can call the method of the interface implementation class, and the added method is also output. In this way, the dynamic agent is realized.
Source code
Proxy. newProxyInstance (classloaderloader, class [] interfaces, invocationhandler h) generates proxy objects, so we go to the implementation of newProxyInstance:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { // Inspection h is not empty Objects.requireNonNull(h); // Copy class object of interface final Class<?>[] intfs = interfaces.clone(); // Do some security checks final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. * Query (already exists in the cache) or generate the class object of the specified proxy class. */ Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } // Get the constructor of the proxy class object. The parameters of this constructor are specified by constructorparams, and the parameters constructorParames are constant values: private static final class <? > [] constructorParams = { InvocationHandler.class }; 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; } }); } // Generate proxy object. The passed in parameter new Object[]{h} will be described later 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); } }
The core of this code is to get the Class object of the proxy Class through getProxyClass0(loader, intfs), then get the construction method through the Class object, and then create the proxy object.
The use of reflection lies in the generation of the dynamic proxy class proxy, that is, the newProxyInstance() method. It implements the second parameter, that is, the interface class. Then, inside the interface method, it calls the invoke() method of the handler through reflection, and the invoke() method passes the internal method invoke() checks the parameters passed in, and then calls the methods of the interface implementation class according to the different parameters.
The implementation of this point in spring AOP is the same, but the dynamic proxy mechanism of AOP has more cglib dynamic proxy, which is used to proxy objects that do not implement interfaces. Beans like this are represented by cglib.
Scan code concerns my WeChat official account: Java architects advanced programming
Get the latest interview questions and share the dry goods of Java technology, including JVM, SpringBoot, SpringCloud, database, architecture design, interview questions, e-books, etc. we look forward to your attention!