The proxy pattern
Proxy Pattern: a class can represent the functions of another class.
In daily life, there are many agency modes, such as real estate agents, entrusted lawyers, overseas purchasing agents, and flash delivery in the same city. They all represent some behaviors of actual users, and greatly provide professionalism, convenience and expansibility.
The class diagram for the proxy pattern is shown below.
Static agent
The following example can be used to illustrate the idea of the proxy pattern.
In ancient times, it was the chief steward who delivered messages when the emperor issued imperial edicts, gave promotions and salary increases to the princes and ministers, and conferred titles to the concubines in the harem. The princes and ministers also had to kowtow honestly to express gratitude. The chief steward was the agent of the emperor.
/** * @author lyqiang */ interface Sayer { void saySomeThing(); } class Emperor implements Sayer {@override public void saySomeThing() {system.out.println (" Emperor implements Sayer {@override public void Sayer () {system.out.println (" Emperor implements Sayer; I am very pleased to award you a promotion of P8 and a monthly salary of $10." ); } } class LiGongGong implements Sayer { Emperor emperor; public LiGongGong(Emperor emperor) { this.emperor = emperor; } @override public void saySomeThing() {system.out.println (" ); emperor.saySomeThing(); System.out.println(" select this." ); }} public class ProxyTest {public static void main(String[] args) {Sayer Sayer = new Emperor()); sayer.saySomeThing(); }}Copy the code
The result is as follows:
The emperor ordered. When the people are at peace, all nations are served. I am very pleased to award you a promotion of P8 with a monthly salary of $10. Qin here.Copy the code
As you can see, the chief steward added a beginning and an end to the emperor’s words, that is, the proxy class can extend the function of the proxy class.
As can be seen from the implementation of static proxy, the proxy class also needs to implement the interface of the proxy class, resulting in a certain code redundancy, and the proxy class can only implement the implementation of a fixed interface class, and its flexibility is not strong.
JDK dynamic proxy
The JDK provides a dynamic proxy approach based on interfaces.
The specific implementation case is as follows.
/** * @author lyqiang */ interface Sayer { void saySomeThing(); } class Emperor implements Sayer {@override public void saySomeThing() {system.out.println (" Emperor implements Sayer {@override public void Sayer () {system.out.println (" Emperor implements Sayer; I am very glad to have turned over my concubine's cards today." ); } } interface Killer { void kill(); } class MotherOfEmperor implements Killer {@override public void kill() {system.out.println (" today, emperor implements Killer." ); } } class GongGongHandler implements InvocationHandler { private final Object obj; public GongGongHandler(Object obj) { this.obj = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String title = "kill".equals(method.getName()) ? "The Queen mother." "Deliver," said the Emperor. ; System.out.println(title); Object result = method.invoke(obj, args); System.out.println(" select this." ); return result; }} public class DynamicProxyTest {public static void main(String[] args) { // Class<? Sayer Sayer = new Emperor(); >[] interfaces Sayer Sayer = new Emperor(); Sayer proxy = (Sayer) Proxy.newProxyInstance(Sayer.class.getClassLoader(), new Class[]{Sayer.class}, new GongGongHandler(sayer)); proxy.saySomeThing(); System. The out. Println (" -- -- -- -- -- -- -- -- -- "); Killer Killer = new MotherOfEmperor(); Killer killProxy = (Killer) Proxy.newProxyInstance(Killer.class.getClassLoader(), new Class[]{Killer.class}, new GongGongHandler(killer)); killProxy.kill(); }}Copy the code
The result is as follows:
The emperor ordered. When the people are at peace, all nations are served. I am very happy, today I love the queen's card. Qin here. —————————————————— Queen Mother Yizhi. Today, I was so angry that I died. Qin here.Copy the code
The code above can be optimized by adding the create agent class to the GongGongHandler class.
class GongGongHandler<T> implements InvocationHandler { private final T obj; public GongGongHandler(T obj) { this.obj = obj; } public T getProxy() { return (T) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String title = "kill".equals(method.getName()) ? "The Queen mother." "Deliver," said the Emperor. ; System.out.println(title); Object result = method.invoke(obj, args); System.out.println(" select this." ); return result; }} public static void main(String[] args) {Sayer sayerProxy = new GongGongHandler<Sayer>(new Emperor()).getProxy(); sayerProxy.saySomeThing(); System. The out. Println (" -- -- -- -- -- -- -- -- -- "); // Killer killProxy = new GongGongHandler<Killer>(new MotherOfEmperor()).getProxy(); killProxy.kill(); }}Copy the code
Can see, the main manager wen can acting emperor issued imperial decree, wu can acting empress dowager killed the imperial concubine.
In contrast to static proxies, JDK dynamic proxies do not have to implement the propped class’s interface, and can proxy multiple types of interfaces.
The key points of the JDK’s dynamic proxy are as follows.
- 1. Core execution logic processor implementation
InvocationHandler
Interface, and implementinvoke()
Methods. - 2, use,
Proxy.newProxyInstance()
To create the proxy class.- ClassLoader Loader that identifies the ClassLoader of the proxied class
- Class
[] interfaces, which identify the interfaces implemented by the proxy class - InvocationHandler H, the handler for the agent
- 3. Convert the proxy class to the type of the proxied class and call the method.
How the JDK dynamic proxy works
Static proxies are those where the proxy relationship is determined at compile time, whereas dynamic proxies are created at program runtime through reflection. Proxy.newproxyinstance () source code below.
public static Object newProxyInstance(ClassLoader loader, Class<? >[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); 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. > cl = getProxyClass0(loader, intfs); // Invoke the designated Invocation invocation. // Invoke the designated Invocation invocation. = null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } 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 ...... }Copy the code
Focus on getProxyClass0.
private static Class<? > getProxyClass0(ClassLoader loader, Class<? >... If (interfaces.length > 65535) {throw new IllegalArgumentException("interface ") limit exceeded"); } // If the proxy class defined by the given loader implementing // the given interfaces exists, this will simply return the cached copy; // Otherwise, it will create the proxy class via the ProxyClassFactory return proxyClassCache. interfaces); }Copy the code
The structure of proxyClassCache is as follows.
private static final WeakCache<ClassLoader, Class<? >[], Class<? >> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());Copy the code
Caches are put in via the apply() method of ProxyClassFactory.
private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<? >[], Class<? >> {// prefix for all proxy class names // proxy class prefix private static Final String proxyClassNamePrefix = "$proxy "; // Next number to use for generation of unique proxy class names private static final AtomicLong nextUniqueNumber = new AtomicLong(); @Override public Class<? > apply(ClassLoader loader, Class<? >[] interfaces) { Map<Class<? >, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<? > intf : interfaces) { // ...... Omit some unimportant code String proxyPkg = null; // package to define proxy class in int accessFlags = Modifier.PUBLIC | Modifier.FINAL; /* * Record the package of a non-public proxy interface so that the * proxy class will be defined in the same package. Verify that * all non-public proxy interfaces are in the same package. */ for (Class<? > intf : interfaces) { int flags = intf.getModifiers(); if (! Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; String name = intf.getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (! pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } if (proxyPkg == null) { // if no non-public proxy interfaces, ProxyPkg = reflectutil. PROXY_PACKAGE + "."; use com.sun.proxy package // Default com.sun.proxy. proxyPkg = reflectutil. } /* * Choose a name for the proxy class to generate. */ long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * Generate the specified proxy class. */ / Generate the specified proxy class ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); Class return defineClass0(loader, proxyName, proxyClassFile, 0, proxyclassfile.length); } catch (ClassFormatError e) { /* * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). */ throw new IllegalArgumentException(e.toString()); }}}Copy the code
As you can see from the code, the class bytecode file is generated based on the information specified by the proxy class. The class file is then used to dynamically create instances to generate proxy objects.
Let’s write the bytecode file to a myproxy.class file to see its structure.
Public class DynamicProxyTest {public static void main(String[] args) throws IOException {// Sayer sayerProxy = new GongGongHandler<Sayer>(new Emperor()).getProxy(); sayerProxy.saySomeThing(); / / generated class bytecode file byte array int accessFlags = Modifier. The PUBLIC | Modifier. The FINAL; byte[] proxyClassFile = ProxyGenerator.generateProxyClass( sayerProxy.getClass().getName(), new Class[]{Sayer.class}, accessFlags); OutputStream OutputStream = new FileOutputStream("./ myproxy.class "); outputStream.write(proxyClassFile); outputStream.close(); }}Copy the code
Decompile myproxy.class to see the code.
public final class $Proxy0 extends Proxy implements Sayer { private static Method m1; private static Method m3; private static Method m2; 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 saySomeThing() throws { try { super.h.invoke(this, m3, (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 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")); m3 = Class.forName("com.lyqiang.proxy.jdkproxy.dynamic.Sayer").getMethod("saySomeThing"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); }}}Copy the code
The visible dynamic Proxy creates a class that inherits from Proxy and implements the Sayer interface, where the saySomeThing() method directly calls the Invoke () method of InvocationHandler.
conclusion
Static proxy follows the proxy mode, reduces the coupling degree of the system to a certain extent, and realizes the expansion and enhancement of functions, but it is not flexible enough.
Based on the above examples and source code analysis, we learned how dynamic proxies work, summarized below.
- 1, implementation,
InvocationHandler
Interface,invoke
Method to enhance the behavior of extended proxied classes. - 2. Use proxy. newProxyInstance to obtain an instance of the Proxy class.
- A. Dynamically generate a class based on the incoming parameters
- B. This class inherits Proxy and implements the interface of the Proxy class
- C. The proxied class interface methods implemented in this class are actually called
InvocationHandler
的invoke
methods
- 3. Call the methods of the proxy class.
Dynamic proxies, while relatively flexible, have the disadvantage that prosted-classes must implement an interface. It is an interface based Proxy, because the generated Proxy class already inherits the Proxy class, and Java is single inheritance, so dynamic extension can only be implemented by implementing the interface.
There is also a class-based dynamic proxy called Cglib, which will be covered next time.
Pay attention to, do not get lost, the old driver does not start regularly.