What is a proxy
The proxy pattern is a common java design pattern. It is characterized by the same interface between the proxy class and the delegate class. The proxy class is mainly responsible for preprocessing messages, filtering messages, forwarding messages to the delegate class, and postprocessing messages for the delegate class.There is usually an association between a proxy class and a delegate class. An object of a proxy class is associated with an object of a delegate class. The object of a proxy class does not really implement the service itself, but provides a specific service by calling the related methods of the object of the delegate class.
Proxy is not only in the field of software development, but also in our daily life.For example, if a P2P boss suddenly runs with his aunt and runs away with money, he has pity on the following group of programmers who are carrying a single home loan. There are older and younger programmers. Programmers can only be forced to apply for labor arbitration. The Labor Bureau will assign an attorney to be solely responsible for the arbitration of programmers (PS:p2p runway arbitration has a very low possibility of getting back the salary, so it is better if you do not return the salaryYes).That is why the agency model is used, because in the activity of labor arbitration, the lawyers'associations act as full agents for programmers.For example: the landowner wants to sell his house, so he goes to the real estate intermediary company to find an intermediary (agent) to help the landowner complete the sale of the house, sign contracts, online signatures, transfer loans, and so on.
proxy pattern
This is a common UML diagram for common proxy patterns.
The following points should be noted:
- Users only care about interface functions, not who provides them.The interface in the diagram above is a Subject.
- The real implementer of the interface is the RealSubject shown above, but it does not come into direct contact with the user but through a proxy.
- The Proxy is the Proxy in the figure above, and because it implements the Subject interface, it can directly contact the user.
- When the user calls Proxy, RealSubject is called internally in Proxy.So Proxy is the intermediary, and it enhances RealSubject operations.
- Proxy can also be divided into static proxy and dynamic proxy.Let's start with a static proxy.
Static Proxy
Movies are commissioned to be played by movie companies, but when the movie is played, the theater can generate some of its own economic benefits, such as providing a massage chair, a doll machine (which can't be clipped up basically every time you go to the movie theater, Mu Da Shen can teach you the trick), selling popcorn, drinks (expensive, you can't eat them anyway).When we usually go to the cinema to see a movie, do we often put advertisements at the beginning of the movie?Then play some advertisements at the beginning and end of the movie.
Here we use the code to simulate the movie theater series of monetization operations.
First, there must be an interface, and a common interface is the basis of the implementation of proxy mode.This interface, named Movie, represents the ability to play movies.
package com.workit.demo.proxy; public interface Movie { void play(); }
- Next we will create a class that actually implements this Movie interface and a proxy class that implements the interface.
Real Captain America movies:
package com.workit.demo.proxy; public class CaptainAmericaMovie implements Movie { @Override public void play() { System.out.println("The movie being shown in the regular studio is Captain America"); } }
Proxy class:
package com.workit.demo.proxy; public class MovieStaticProxy implements Movie { Movie movie; public MovieStaticProxy(Movie movie) { this.movie = movie; } @Override public void play() { playStart(); movie.play(); playEnd(); } public void playStart() { System.out.println("Advertisements are playing before the movie starts"); } public void playEnd() { System.out.println("The movie is over, follow up with advertisements"); } }
Test class:
package com.workit.demo.proxy; package com.workit.demo.proxy; public class StaticProxyTest { public static void main(String[] args) { Movie captainAmericaMovie = new CaptainAmericaMovie(); Movie movieStaticProxy = new MovieStaticProxy(captainAmericaMovie); movieStaticProxy.play(); } }
Run result:
Advertisements are playing before the movie starts The movie being played is Captain America The movie is over, follow up with advertisements
Now you can see that the proxy mode can add and enhance some functions by extending the proxy classes without modifying the proxy objects.It is worth noting that proxy classes and proxied classes should either implement an interface together or inherit a class together.This is the content of the static proxy. Why is it called static?Because its type is predefined, such as the MovieStaticProxy class in the code above.
Advantage
- Proxy mode acts as an intermediary between the client and the target object and protects the target object
- Proxy objects extend the capabilities of the target object
- The proxy mode separates the client from the target object and reduces the coupling of the system to some extent.
shortcoming
- Proxy objects need to implement the same interface as the target object, so there will be many proxy classes, too many classes. At the same time, once the interface adds methods, both the target object and the proxy object will be maintained.
jdk dynamic proxy
In contrast to static proxy classes, dynamic proxy classes have byte codes that are generated dynamically by the Java reflection mechanism while the program is running without the need for programmers to write their source code manually.Dynamic proxy classes not only simplify programming, but also improve the scalability of software systems, because Java reflection mechanisms can generate any type of dynamic proxy class.Java.lang.reflectThe Proxy class in the package and the InvocationHandler interface provide the ability to generate dynamic proxy classes.
- Next to the example above, I just finished watching Captain America and I want to go on to see Iron Man.It doesn't make sense to have been watching movies in a regular cinema, so go to the VIP Studio (I don't know what it looks like yet) and have a try.Since physical stores haven't experienced it, try it with code.Create a VIPMovie movie interface
package com.workit.demo.proxy; public interface VIPMovie { void vipPlay(); }
Next, create a playback implementation class for the VIP Studio
package com.workit.demo.proxy; public class IronManVIPMovie implements VIPMovie { @Override public void vipPlay() { System.out.println("VI The movie being shown in the cinema is Iron Man"); } }
If we follow the static proxy and want to create a proxy implementation class for VIP Studio playback, we won't demonstrate this.Let's see how this can be achieved through dynamic proxies.
package com.workit.demo.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MyInvocationHandler implements InvocationHandler { private Object object; public MyInvocationHandler(Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { playStart(); Object invoke = method.invoke(object, args); playEnd(); return invoke; } public void playStart() { System.out.println("Advertisements are playing before the movie starts"); } public void playEnd() { System.out.println("The movie is over, follow up with advertisements"); } }
MyInvocationHandler implements the class InvocationHandler. What does this class mean?Don't panic, I'll explain below.Then we can watch the movie in the VIP Cinema.
package com.workit.demo.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class DynamicProxyTest { public static void main(String[] args) { IronManVIPMovie ironManVIPMovie = new IronManVIPMovie(); InvocationHandler invocationHandler = new MyInvocationHandler(ironManVIPMovie); VIPMovie dynamicProxy = (VIPMovie) Proxy.newProxyInstance(IronManVIPMovie.class.getClassLoader(), IronManVIPMovie.class.getInterfaces(), invocationHandler); dynamicProxy.vipPlay(); } }
Output results:
Advertisements are playing before the movie starts The movie being shown in VI Studio is Iron Man The movie is over, follow up with advertisements
See no, we did not implement a proxy class for the VIPMovie interface like a static proxy, but eventually it still performs the same function. The difference is why the dynamic proxy discussed earlier is so-called "dynamic".
Let's also use dynamic proxy to implement Captain America by the way.
package com.workit.demo.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class DynamicProxyTest { public static void main(String[] args) { // VIP Studio Iron Man IronManVIPMovie ironManVIPMovie = new IronManVIPMovie(); InvocationHandler invocationHandler = new MyInvocationHandler(ironManVIPMovie); VIPMovie dynamicProxy = (VIPMovie) Proxy.newProxyInstance(IronManVIPMovie.class.getClassLoader(), IronManVIPMovie.class.getInterfaces(), invocationHandler); dynamicProxy.vipPlay(); // Common Studio Captain America CaptainAmericaMovie captainAmericaMovie = new CaptainAmericaMovie(); InvocationHandler invocationHandler1 = new MyInvocationHandler(captainAmericaMovie); Movie dynamicProxy1 = (Movie) Proxy.newProxyInstance(CaptainAmericaMovie.class.getClassLoader(), CaptainAmericaMovie.class.getInterfaces(), invocationHandler1); dynamicProxy1.play(); } }
Output results:
Advertisements are playing before the movie starts The movie being shown in VI Studio is Iron Man The movie is over, follow up with advertisements Advertisements are playing before the movie starts The movie being played is Captain America The movie is over, follow up with advertisements
We pass theProxy.newProxyInstanceThe () method produces an implementation class proxy for both Movie and VIPMovie interfaces, which is the magic of dynamic proxy.
How exactly does the JDK dynamic proxy work?
Dynamic code involves a very important class, Proxy.It is through the static method newProxyInstance of Proxy that the proxy is dynamically created.How to create a proxy class is not analyzed. If you are interested, you can look at the source code.Let's look directly at the generated proxy class.
How do I view the generated proxy classes?
Add the following code (jdk1.8 I used) before generating the proxy class:
//Proxy class generated by new version of jdk System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
If the above code is not valid, consider adding the following code:
// Old version jdk System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); // This setting is used to output classes generated by the cglib dynamic proxy System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\class");
The code is as follows:
public static void main(String[] args) { //Proxy class generated by new version of jdk System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true"); // VIP Studio Iron Man IronManVIPMovie ironManVIPMovie = new IronManVIPMovie(); InvocationHandler invocationHandler = new MyInvocationHandler(ironManVIPMovie); VIPMovie dynamicProxy = (VIPMovie) Proxy.newProxyInstance(IronManVIPMovie.class.getClassLoader(), IronManVIPMovie.class.getInterfaces(), invocationHandler); dynamicProxy.vipPlay(); // Common Studio Captain America CaptainAmericaMovie captainAmericaMovie = new CaptainAmericaMovie(); InvocationHandler invocationHandler1 = new MyInvocationHandler(captainAmericaMovie); Movie dynamicProxy1 = (Movie) Proxy.newProxyInstance(CaptainAmericaMovie.class.getClassLoader(), CaptainAmericaMovie.class.getInterfaces(), invocationHandler1); dynamicProxy1.play(); System.out.println("VIP Proxy category for Film Hall Iron Man:"+dynamicProxy.getClass()); System.out.println("Common Studio Captain America:"+dynamicProxy1.getClass()); }
We can see the results
Advertisements are playing before the movie starts The movie being shown in VI Studio is Iron Man The movie is over, follow up with advertisements Advertisements are playing before the movie starts The movie being played is Captain America The movie is over, follow up with advertisements VIP Studio Iron Man Proxy Class:Com.sun.proxy. $Proxy0 Common Studio Captain America:classCom.sun.proxy. $Proxy1
Two proxy classes were generated, $Proxy0 and $Proxy1, respectively.
Let's look at the proxy class for Iron Man, $Proxy0
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.sun.proxy; import com.workit.demo.proxy.VIPMovie; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy0 extends Proxy implements VIPMovie { 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 vipPlay() 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.workit.demo.proxy.VIPMovie").getMethod("vipPlay"); 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()); } } },
From the above code, we can see that $Proxy0 extends Proxy implements VIPMovie inherits Proxy and implements VIPMovie interface, which is why jdk dynamic proxy must be interface based and java is single inherited.
Then look at how the proxy class implements:
public final void vipPlay() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
The invoke method for h in this supper.h.invoke Proxy isInvocationHandler.invokeThat is, above MyInvocationHandler.invoke Method, the whole process is clear.This is jdk's dynamic proxy.
cglib dynamic proxy
As mentioned above, jdk dynamic proxy can only be based on interfaces, so what if it is a class that needs dynamic proxy?The cglib dynamic proxy solves the dynamic proxy for classes.
Let's create a Captain America 2
package com.workit.demo.proxy; public class CaptainAmerica2MovieImpl { public void play(){ System.out.println("The movie being played is Captain America 2"); } }
Introducing cglib pom dependency
<!-- https://mvnrepository.com/artifact/cglib/cglib --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency>
Create a custom MethodInterceptor.
package com.workit.demo.proxy; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CglibProxyInterceptor implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { playStart(); Object object = methodProxy.invokeSuper(o, objects); playEnd(); return object; } public void playStart() { System.out.println("Advertisements are playing before the movie starts"); } public void playEnd() { System.out.println("The movie is over, follow up with advertisements"); } }
Test Class
package com.workit.demo.proxy; import net.sf.cglib.core.DebuggingClassWriter; import net.sf.cglib.proxy.Enhancer; public class CglibProxyTest { public static void main(String[] args) { // //Generate dynamic proxy classes in specified directory System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\class"); //Creating an Enhancer object, similar to the Proxy class of the JDK dynamic proxy, the next step is to set several parameters Enhancer enhancer = new Enhancer(); //Set Byte Code File for Target Class enhancer.setSuperclass(CaptainAmerica2MovieImpl.class); //Set Callback Function enhancer.setCallback(new CglibProxyInterceptor()); //The create method here is to formally create the proxy class CaptainAmerica2MovieImpl captainAmerica2Movie = (CaptainAmerica2MovieImpl)enhancer.create(); //Call the play method of the proxy class captainAmerica2Movie.play(); System.out.println("cglib Dynamic agent Captain America 2:"+captainAmerica2Movie.getClass()); } }
Output results:
Advertisements are playing before the movie starts The movie being played is Captain America 2 The movie is over, follow up with advertisements cglib dynamic proxy Captain America 2:classCom.workit.demo.Proxy.CaptainAmerica2MovieImpl$$EnhancerByCGLIB$$5c3ddcfe
Let's look at the play method generated by the eventually created proxy class
public class CaptainAmerica2MovieImpl$$EnhancerByCGLIB$$5c3ddcfe extends CaptainAmerica2MovieImpl implements Factory { public final void play() { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (var10000 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if (var10000 != null) { var10000.intercept(this, CGLIB$play$0$Method, CGLIB$emptyArgs, CGLIB$play$0$Proxy); } else { super.play(); } }
As you can see from decompiling the source code of the proxy object, the proxy object inherits from CaptainAmerica2MovieImpl, and the intercept() method is called by the intercept intercept() interceptor.
The intercept() method is implemented by a custom CglibProxyInterceptor, so the intercept() method in CglibProxyInterceptor is finally called to complete the dynamic proxy implementation from the proxy object to the target object.
- CGlib is a powerful, high-performance, high-quality Code Generation Class Library.It can extend Java classes and implement Java interfaces at runtime.
- Generating proxy classes with CGlib is a subclass of the target class.
- No interface is required to generate proxy classes with CGlib.
- The proxy class generated by CGLib overrides the methods of the parent class.
- The intercept method content in the interceptor is exactly the method body in the proxy class.
summary
- Proxies are divided into static proxy and dynamic proxy.
- Static proxy, proxy classes need to write their own code.
- Dynamic proxies are jdk and cglib, and proxy classes pass through Proxy.newInstance() or ASM generation.
- The difference between static and dynamic proxies is that you don't want the developer to define the Proxy class.
Dynamic proxy generates proxy class dynamically through Proxy, but it also specifies an implementation class for InvocationHandler or MethodInterceptor.
- The proxy mode is essentially designed to enhance the functionality of existing code.
End
- Since you are ignorant, you will inevitably have faults. If you find something wrong, you can leave a message to point it out to me, and I will correct it.
- If you think the article is good, your forwarding, sharing, admiring, complimenting, leaving a message is my greatest encouragement.
- Thank you for your reading and we appreciate your interest.
Reference resources
https://blog.csdn.net/m0_37314675/article/details/77850967
https://www.cnblogs.com/cC-Zhou/p/9525638.html
https://www.jianshu.com/p/4539e6d9f337