Case
Xiao Zhang is an ordinary code farm, hard code every day. One day at noon, Xiao Zhang was just going to eat, a phone call to his cell phone. "Is xx company's small Zhang?" I am the king of yy company AA ". "Oh, it's Wang, what's the matter?" ”。 After the communication, Xiao Zhang figure out that the original customer has a demand, just responsible for the development of the small Zhang, the customer directly found him. But the small Zhang but did not agree to the customer's request, but let customers find product manager Xiao Li communication.
Is Xiao Zhang anxious to eat noodles and shake the pot? Not, just so that the story can be nested in the proxy mode. Let's take a look at the definition of proxy mode: * Provides a proxy for other objects to control access to this object. (Provide a surrogate or placeholder for another object to control access to it)
In contrast to the definition, the code farmer can be mapped to other objects, product manager Xiao Li for the agent of Xiao Zhang. We use Java code to express the above examples.
Static proxy 1. Abstract roles
Based on the idea of object-oriented, we first define a code farm interface, which has a method to realize user's requirement.
Public interface Icoder {public void impldemands (String demandname);
2. Real characters
We assume that Xiao Zhang is a java programmer who defines a Java code farm that implements the requirements through the Jaa language.
public class Javacoder implements icoder{ private String name; Public Javacoder (String name) { this.name = name; } @Override public void impldemands (String demandname) { System.out.println (name + "implemented demand:" + Demandname + "in java!" ); }}
3. Proxy roles
Wronged a product manager, named it as the code Farm agent class, while allowing him to implement the Icoder interface.
public class Coderproxy implements icoder{ private icoder coder; Public Coderproxy (Icoder coder) { this.coder = coder; } @Override public void impldemands (String demandname) { coder.impldemands (demandname);} }
The above one interface, two classes, the implementation of the proxy mode. Is you kidding me? So simple? Yes, it's that simple. We use a scenario class to simulate users looking for product managers to increase demand.
public class Customer {public static void main (String args[]) { //define a Java code farm Icoder coder = new JavaC oder ("Zhang"); Define a product manager Icoder proxy = new coderproxy (coder); Let the product manager achieve a requirement proxy.impldemands (); }}
Run the program with the following results:
Zhang implemented Demand:add user manageMent in java!
The Product Manager acts as a programmer's agent, and the customer tells the product manager that it doesn't need to be in touch with the programmer. Seen here, some witty programmers found the problem. You see, the product manager conveyed the customer's needs, no wonder I looked at the product manager so upset.
The product manager is certainly not just conveying the user's needs, he has a lot of things to do. For example, the project decided not to accept the need for new features, and made some modifications to the Coderproxy class:
public class Coderproxy implements icoder{ private icoder coder; Public Coderproxy (Icoder coder) { this.coder = coder; } @Override public void impldemands (String demandname) { if (Demandname.startswith ("Add")) { System.out.println ("No longer receive ' Add ' demand"); return; Coder.impldemands (Demandname);}}
In this way, when the customer has increased the demand for functionality, the product manager directly rebuffed, the programmer no longer need to filter this part.
Summarize
Let's make a simple abstraction of the above example:
The proxy mode contains the following roles:
- Subject: Abstract theme role. Can be an interface, or it can be an abstract class.
- Realsubject: Real theme role. The specific performer of the business logic.
- Proxysubject: Agent topic role. Internal contains realsubject reference, responsible for the real role of the call, and before and after the real-world theme role processing and preprocessing and aftercare work.
Agent Mode Advantages:
- Responsibility clear real role only need to focus on the implementation of business logic, non-business logic part, later through the proxy class can be completed.
- High scalability no matter how the real role changes, because the interface is fixed, the proxy class does not need to make any changes.
Dynamic Agent
The main one is the static proxy. So what is a dynamic agent?
Suppose there is a requirement to print the system time before the method executes and after execution completes. This is very simple, non-business logic, as long as the proxy class calls the real role of the method before and after the output time is possible. Like the example above, there is only one impldemands method, so the implementation is no problem. But if the real character has 10 methods, then we have to write 10 times exactly the same code. A bit of the pursuit of the yard, will certainly be very uncomfortable with this method. Some witty little companions may have thought of solving this problem with AOP. Very correct. What is the relationship between AOP and dynamic proxies? That's right! AOP is a dynamic proxy.
The proxy class that is created when the program runs is called a dynamic proxy. In other words, the proxy class does not need to be defined in Java code, but is generated dynamically at run time. Compared with static proxy, the advantage of dynamic agent is that the function of proxy class can be handled uniformly, without modifying the function of each proxy class. For the requirements of the above example printing time, by using dynamic proxies, we can do a "unified instruction" to unify the methods of all proxy classes without modifying each method individually. Below we will specifically describe how to use dynamic proxy approach to achieve our needs.
Abstract roles and real roles do not change compared to static proxies. Only proxy classes are changed. Therefore, abstract characters, real characters, reference Icoder and JAVACODR.
When using dynamic proxies, we need to define a mediation class that is located between the proxy class and the delegate class, also called the dynamic proxy class, which is required to implement the Invocationhandler interface:
public class Coderdynamicproxy implements invocationhandler{ //proxy instance private icoder coder; Public Coderdynamicproxy (Icoder _coder) { this.coder = _coder; } Call the Proxy method @Override Public object Invoke (Object proxy, Method method, object[] args) throws Throwable {System.out.println (System.currenttimemillis ()); Object result = Method.invoke (coder, args); System.out.println (System.currenttimemillis ()); return result;}}
When we invoke the method of the proxy class object, this "call" is forwarded to the mediation class's Invoke method, and the parameter method identifies which method of the proxy class we specifically call, and args is the parameter of the method.
We use a scenario class to simulate a user looking for a product manager to change requirements.
public class Dynamicclient {public static void main (String args[]) { //Real object to proxy icoder coder = new Ja Vacoder ("Zhang"); Create a Mediation class instance Invocationhandler handler = new coderdynamicproxy (coder); Gets the class loader ClassLoader cl = coder.getclass (). getClassLoader (); Dynamically generates a proxy class Icoder proxy = (Icoder) proxy.newproxyinstance (cl, Coder.getclass (). Getinterfaces (), handler); Through the proxy class, execute the DoSomething method; Proxy.impldemands ("Modify User Management");}}
The results of the implementation are as follows:
1501728574978Zhang implemented demand:modify user management in java!1501728574979
With the code above, the time before and after all the methods of the delegate class is implemented. Or the familiar little Zhang, but we did not create the proxy class, nor time Icoder interface. This is the dynamic proxy.
Summarize
To summarize, a typical dynamic agent can be divided into the following four steps:
- Creating abstract roles
- Create a real role
- Creating a mediation class by implementing the Invocationhandler interface
- Dynamic generation of proxy classes through scene classes
If you just want to use a dynamic agent, see here is enough. But if you want to know why the proxy object is able to execute the invoke method of the mediation class, and what the generated proxy object is, you can continue to look down.
Source Analysis (JDK7)
See the small partners here, are the pursuit of programmers. Above the scene class, by
Dynamically generates a proxy class Icoder proxy = (icoder) proxy.newproxyinstance (cl, Coder.getclass (). Getinterfaces (), handler);
A proxy class was generated dynamically. So how is this proxy class generated? We have a glimpse through the code.
The Newproxyinstance method of the proxy class, the main business logic is as follows:
Generate the proxy classes class and load into the JVM class<?> cl = GETPROXYCLASS0 (loader, interfaces);// Gets the constructor for the proxy class parameter Invocationhandler final constructor<?> cons = cl.getconstructor (constructorparams);//Generate proxy class, and returns return Newinstance (cons, IH);
The above code does three things:
- Dynamically generates a class based on the incoming parameter interfaces, which implements the interface in the interfaces, which is the Impldemands method of the Icoder interface. Assume that the dynamically generated class is $proxy0.
- The newly generated $PROXY0 class is loaded into the JVM through the incoming classloder.
- Using the Mediation class, call $proxy0 's $proxy0 (Invocationhandler) constructor, create an instance of the $proxy0 class, its Invocationhandler property, and create the mediation class for us.
The core of the above lies in the GetProxyClass0 method:
private static class<?> getProxyClass0 (ClassLoader loader, class<?>... interfaces) { 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 would simply retur n the cached copy; Otherwise, it'll create the proxy class via the Proxyclassfactory return proxyclasscache.get (loader, Interf Aces); }
There is a property in the proxy class Proxyclasscache, which is a static variable of type Weakcache. It indicates the mapping between the class loader and the proxy class. So the Proxyclasscache get method is used to get the proxy class based on the ClassLoader, and if it already exists, it is returned directly from the cache, and if not, a map is created and the cache table is updated.
Let's follow the creation process for the proxy class:
Call the Get method of the factory class, and it calls the Proxyclassfactory class's apply method, and finally finds the following line of code:
Generate the specified proxy class.byte[] proxyclassfile = Proxygenerator.generateproxyclass (proxyname, interfaces);
That's it, the proxy class is generated.
To view dynamically generated proxy classes
With the above analysis, we already know that the proxy class dynamically creates the process of the agent class. What exactly is the proxy class that was created? We can generate it manually by using the following code:
public class Codeutil {public static void Main (string[] args) throws IOException { byte[] classfile = Pro Xygenerator.generateproxyclass ("Testproxygen", Javacoder.class. getinterfaces ()); File File = new file ("D:/aaa/testproxygen.class"); FileOutputStream fos = new fileoutputstream (file); Fos.write (classfile); Fos.flush (); Fos.close (); } }
To view the generated class file by using the Anti-compilation tool:
ImportJava.lang.reflect.invocationhandler;importJava.lang.reflect.method;importJava.lang.reflect.proxy;importJava.lang.reflect.undeclaredthrowableexception;importModel.proxy.ICoder; Public final class Testproxygen extendsProxy Implementsicoder{private staticMethod M1; private staticMethod M0; private staticMethod m3; private staticMethod m2; PublicTestproxygen (Invocationhandler Paraminvocationhandler) throws{Super(Paraminvocationhandler); } Public Final BooleanEquals (Object Paramobject) throws{Try{return (Boolean) This.h.invoke (this, M1, newObject[] {paramobject}). Booleanvalue (); } catch(RuntimeException localruntimeexception) {ThrowLocalruntimeexception; } catch(Throwable localthrowable) {} throw newUndeclaredthrowableexception (localthrowable); } Public Final intHashcode () throws{Try{return ((Integer) This.h.invoke (this, M0, null). Intvalue (); } catch(RuntimeException localruntimeexception) {ThrowLocalruntimeexception; } catch(Throwable localthrowable) {} throw newUndeclaredthrowableexception (localthrowable); } public final voidImpldemands (String paramstring) throws{Try{This.h.invoke (this, M3, newObject[] {paramstring}); Return; } catch(RuntimeException localruntimeexception) {ThrowLocalruntimeexception; } catch(Throwable localthrowable) {} throw newUndeclaredthrowableexception (localthrowable); } Public FinalString toString () throws{Try{return (String) This.h.invoke (this, M2, null); } catch(RuntimeException localruntimeexception) {Throw localruntimeexception;} Catch (Throwable localthrowable) {} throw new undeclaredthrowableexception (localthrowable);} static {Try {m1 = Class.forName ("Java.lang.Object"). GetMethod ("equals", new class[] {class.forname ("Java.lang.Object" )}); M0 = Class.forName ("Java.lang.Object"). GetMethod ("Hashcode", new class[0 ]); m3 = Class.forName (" Model.proxy.ICoder "). GetMethod (" Impldemands ", new class[] {class.forname (" java.lang.String ")}); m2 = Class.forName ("Java.lang.Object"). GetMethod ("ToString", new class[0 ]); return ;} catch ( Nosuchmethodexception localnosuchmethodexception) {throw new Nosuchmethoderror ( Localnosuchmethodexception.getmessage ()); } catch (ClassNotFoundException localclassnotfoundexception) {} throw new Noclassdeffounderror ( Localclassnotfoundexception.getmessage ()); }}
In this way, we understand why invoking the Impldemands method of the proxy class goes back to executing the Invoke method of the mediation class.
Java proxy mode