Let's analyze the word agent first. Agent
The agent was translated by proxy in English. We have seen in the life of agents, probably the most common is the circle of friends to sell facial mask classmate.
They take the goods from the factory and then advertise them in their circle of friends and sell them to acquaintances.
Logically speaking, customers can buy products directly from the manufacturers, but in real life, there are few such sales model. Generally are manufacturers commissioned to sell agents, customers and agents to deal with, and not directly related to the actual production producers.
So, the agent has a taste of the middle man.
Next, we talk about the agent mode in the software. Agent Mode
Agent mode is a more common design pattern in object-oriented programming.
This is a common UML diagram of common proxy patterns.
There are a few things to note: The user cares only about interface functionality, not who provides the functionality. The interface in the above image is Subject. The real implementation of the interface is the realsubject of the image above, but it does not contact the user directly, but through the proxy. The proxy is the proxy in the above diagram, because it implements the Subject interface, so it can contact with the user directly. When the user invokes proxy, Realsubject is invoked inside the proxy. So, a Proxy is an intermediary, and it can enhance realsubject operations.
If it's difficult to understand, I'll explain it by example. It is worth noting that the agent can be divided into static agent and dynamic agent two kinds. Start with the static agent. Static proxy
We usually go to the cinema to see the film, at the beginning of the film is not often put ads.
Movies are commissioned by studios to play in theaters, but theaters can generate some of their own financial benefits when they play movies, such as selling popcorn, cola, and then playing some ads at the end of the movie.
Now use the code to simulate.
First of all, there must be an interface, the common interface is the basis for the implementation of proxy mode. This interface is named Movie, representing the ability to play the movie.
Package com.frank.test;
Public interface Movie {
void play ();
}
Then we want to have a real class that implements this Movie interface, and a proxy class that simply implements the interface.
Package com.frank.test;
public class Realmovie implements Movie {
@Override public
Void Play () {
//TODO auto-generated method Stub
system.out.println ("You are watching The Shawshank Redemption");
}
This represents the real film. It implements the Movie interface, and when the play () method is invoked, the movie starts playing. So what about proxy proxies.
Package com.frank.test;
public class Cinema implements Movie {
Realmovie Movie;
Public cinema (Realmovie movie) {
super ();
This.movie = movie;
}
@Override public
Void Play () {
Guanggao (true);
Movie.play ();
Guanggao (false);
public void Guanggao (Boolean isstart) {
if (isstart) {
System.out.println ("The movie starts right away, popcorn, cola, gum 98 percent, come and buy it.") ");
} else {
System.out.println ("The movie is over, popcorn, cola, gum 98 percent, buy it home.") ");
}
}
}
Cinema is the proxy object, which has a play () method. However, when the play () method is invoked, it deals with a number of related benefits, that is, advertising. Now we write the test code.
Package com.frank.test;
public class Proxytest {public
static void Main (string[] args) {
Realmovie Realmovie = new Realmovie ();
Movie Movie = new Cinema (realmovie);
Movie.play ();
}
Then observe the results:
The movie starts right away, popcorn, cola, gum 98 percent, come and buy it.
You're watching The Shawshank Redemption
movie is almost over, popcorn, cola, gum 98 percent, buy it home and eat it.
Now we can see that the proxy mode can be added and enhanced by extending the proxy class without modifying the proxy object. It should be noted that the proxy class and the proxy class should implement an interface together or inherit a class together.
The above describes the static agent content, why is called static. Because its type is predetermined, such as the cinema in the code above. The next thing to introduce is the dynamic proxy. Dynamic Proxy
Since is the proxy, then it and the static proxy function and the goal is no difference, the only difference is the dynamic and static difference.
So where is this dynamic in the dynamic proxy?
The cinema class in the previous code is a proxy, and we need to manually write code to let cinema implement the Movie interface, while in a dynamic proxy we can let the program automatically create an agent in memory that implements the Movie interface, without having to define the cinema class. That's why it's called dynamic.
Perhaps the concept is more abstract. Now the example illustrates the situation.
Suppose there is a big shopping mall, shopping malls have a lot of counters, there is a counter to sell Maotai. We do the code simulation.
Package com.frank.test;
Public interface Sellwine {
void Mainjiu ();
}
Sellwine is an interface that you can understand as a license for liquor.
Package com.frank.test;
public class Maotaijiu implements Sellwine {
@Override public
void Mainjiu () {
//TODO auto-generated method s Tub
System.out.println ("I sell Maotai.") ");
}
}
Then create a class Maotaijiu, right, is the meaning of Maotai.
We also need a counter to sell the liquor:
Package com.frank.test;
Import Java.lang.reflect.InvocationHandler;
Import Java.lang.reflect.Method;
public class Guitaia implements Invocationhandler {
private Object Pingpai;
Public Guitaia (Object pingpai) {
This.pingpai = Pingpai;
}
@Override Public
Object Invoke (Object Proxy, Method method, object[] args)
throws Throwable {/
/TODO Auto -generated method Stub
System.out.println ("Sales Start counter is:" +this.getclass (). Getsimplename ());
Method.invoke (Pingpai, args);
System.out.println ("End of Sale");
return null;
}
}
Guitaia implements the Invocationhandler class, what does this class mean? Don't panic, I'll explain later.
Then we'll be able to sell the liquor.
Package com.frank.test;
Import Java.lang.reflect.InvocationHandler;
Import Java.lang.reflect.Proxy;
public class Test {public
static void Main (string[] args) {
//TODO auto-generated method stub
Maotaijiu maot Aijiu = new Maotaijiu ();
Invocationhandler jingxiao1 = new Guitaia (maotaijiu);
Sellwine dynamicproxy = (sellwine) proxy.newproxyinstance (MaotaiJiu.class.getClassLoader (),
MaotaiJiu.class.getInterfaces (), jingxiao1);
Dynamicproxy.mainjiu ();
}
Here, we have come into contact with a new concept, there is no relationship, let alone, first look at the results.
Sales Start counter is: Guitaia
I sell Maotai.
Sales End
See no, I did not like the static proxy for the Sellwine interface to implement a proxy class, but ultimately it still implements the same function, which is the difference between the dynamic agent discussed before the so-called "dynamic" reasons. Dynamic Proxy Syntax
Let's take it easy, let's start with the grammar, the syntax is very simple.
Dynamic code involves a very important class Proxy. It is the static method Newproxyinstance through proxy that creates the proxy dynamically. Proxy
public static Object newproxyinstance (ClassLoader loader,
class<?>[] interfaces,
Invocationhandler h)
Here's a 3-parameter meaning. Loader is naturally the class loader interfaces code to be used to broker the interface h a Invocationhandler object
Beginners should be unfamiliar with invocationhandler, and I'll talk about it right away. Invocationhandler
Invocationhandler is an interface, the official document explains that each agent instance has a Invocationhandler implementation class associated with it, and if the proxy method is invoked, then the agent notifies and forwards the internal Invocationhandler implementation class, which it decides to handle.
Public interface Invocationhandler {public
object Invoke (Object proxy, Method method, object[] args)
throws Thro wable;
}
Invocationhandler internal is just an invoke () method, it is this method determines how to handle the proxy pass over the method call. Proxy proxy Object method agent object invocation args in the method called
Because proxy dynamically generated proxies invoke the Invocationhandler implementation class, Invocationhandler is the actual performer.
public class Guitaia implements Invocationhandler {
private Object Pingpai;
Public Guitaia (Object pingpai) {
This.pingpai = Pingpai;
}
@Override Public
Object Invoke (Object Proxy, Method method, object[] args)
throws Throwable {/
/TODO Auto-ge nerated method Stub
System.out.println ("Sales Start counter is:" +this.getclass (). Getsimplename ());
Method.invoke (Pingpai, args);
System.out.println ("End of Sale");
return null;
}
}
Guitaia is actually where the liquor is.
Now, we increase the difficulty, we not only to sell Maotai, but also want to sell Wuliangye.
Package com.frank.test;
public class Wuliangye implements Sellwine {
@Override public
void Mainjiu () {
//TODO auto-generated Meth OD stub
System.out.println ("I sell Wu Liang Ye.") ");
}
}
Wuliangye This class also implements the Sellwine interface, stating that it also has a license to sell liquor, and also put it on the Guitaia for sale.
public class Test {public
static void Main (string[] args) {
//TODO auto-generated method stub
Maotaijiu maot Aijiu = new Maotaijiu ();
Wuliangye Wu = new Wuliangye ();
Invocationhandler jingxiao1 = new Guitaia (maotaijiu);
Invocationhandler Jingxiao2 = new Guitaia (WU);
Sellwine dynamicproxy = (sellwine) proxy.newproxyinstance (MaotaiJiu.class.getClassLoader (),
MaotaiJiu.class.getInterfaces (), jingxiao1);
Sellwine dynamicProxy1 = (sellwine) proxy.newproxyinstance (MaotaiJiu.class.getClassLoader (),
MaotaiJiu.class.getInterfaces (), JINGXIAO2);
Dynamicproxy.mainjiu ();
Dynamicproxy1.mainjiu ();
}
Let's see the results:
Sales Start counter is: Guitaia
I sell Maotai.
sales End
Sales start counter is: Guitaia
I sell is Wu Liang Ye.
Sales End
Some may ask, dynamicproxy and dynamicProxy1 What difference does not have. They are dynamically generated agents, all sales assistants, who have a technical certificate for liquor.
I now expand the operation of the shopping malls, in addition to selling alcohol, but also sell cigarettes.
First, you also create an interface as a license to sell cigarettes.
Package com.frank.test;
Public interface Sellcigarette {
void Sell ();
}
And then, what kind of cigarettes are you selling? I am a Hunan people, then Lotus King is good.
public class Furongwang implements Sellcigarette {
@Override public
void Sell () {
//TODO auto-generated Meth OD stub
System.out.println ("Sells authentic hibiscus king, can scan barcode verification.") ");
}
}
Then test the validation again:
Package com.frank.test;
Import Java.lang.reflect.InvocationHandler;
Import Java.lang.reflect.Proxy; public class Test {public static void main (string[] args) {//TODO auto-generated method stub Maot
Aijiu Maotaijiu = new Maotaijiu ();
Wuliangye Wu = new Wuliangye ();
Furongwang fu = new Furongwang ();
Invocationhandler jingxiao1 = new Guitaia (MAOTAIJIU);
Invocationhandler Jingxiao2 = new Guitaia (WU);
Invocationhandler Jingxiao3 = new Guitaia (FU); Sellwine dynamicproxy = (sellwine) proxy.newproxyinstance (MaotaiJiu.class.getClassLoader (), Maotaijiu.clas
S.getinterfaces (), JINGXIAO1); Sellwine dynamicProxy1 = (sellwine) proxy.newproxyinstance (MaotaiJiu.class.getClassLoader (), MAOTAIJIU.CLA
Ss.getinterfaces (), JINGXIAO2);
Dynamicproxy.mainjiu ();
Dynamicproxy1.mainjiu (); Sellcigarette DynamicProxy3 = (sellcigarette) proxy.newproxyinstance (FURONGWANG.CLASs.getclassloader (), Furongwang.class.getInterfaces (), JINGXIAO3);
Dynamicproxy3.sell (); }
}
Then, look at the results:
Sales Start counter is: Guitaia
I sell Maotai.
sales End
Sales start counter is: Guitaia
I sell is Wu Liang Ye.
sales End
Sales start counter is: Guitaia
is selling authentic Hibiscus king, can scan bar code verification.
Sales End
The results are in line with expectations. We carefully observe the code, the same is through the Proxy.newproxyinstance () method, but produced sellwine and sellcigarette two kinds of interface implementation Class agent, this is the magic of dynamic agent. The secret of dynamic agent
There must be some students interested in why proxies can dynamically generate agents of different interface types, my guess is that an interface instance will be generated dynamically through the incoming interface and then through reflection.
For example, Sellwine is an interface, then Proxy.newproxyinstance () will certainly have
New Sellwine ();
This same code, but it is created by reflection mechanism. So that's not the case. Directly check their source code well. To be explained, my current view of the source code is 1.8 version.
public static Object newproxyinstance (ClassLoader loader, class<?>[] Inter
Faces, Invocationhandler h) throws IllegalArgumentException {
Objects.requirenonnull (h);
Final class<?>[] Intfs = Interfaces.clone ();
* * Look up or generate the designated proxy class.
* * class<?> CL = GETPROXYCLASS0 (loader, intfs);
* * Invoke its constructor with the designated invocation handler.
* * Try {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);
The catch (Nosuchmethodexception e) {throw new Internalerror (E.tostring (), E); }
}
Newproxyinstance did create an instance of reflection generated by the construction method of the CL Class file. CL is obtained by 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, this'll exists return The cached copy;
Otherwise, it would create the proxy class via the Proxyclassfactory return
proxyclasscache.get (loader, interfaces);
}
Get directly through the cache, and if not, the comment will be generated via proxyclassfactory.
/** * A factory function that generates, defines and returns the proxy class given * The ClassLoader and array o
F interfaces. * Private static Final class Proxyclassfactory implements Bifunction<classloader, class<?>[], class& lt;?
>> {//Proxy class prefix is "$Proxy", private static final String Proxyclassnameprefix = "$Proxy"; Next number to use for generation of the unique proxy class names private static final Atomiclong Nextuniq
Uenumber = new Atomiclong (); @Override public class<?> Apply (ClassLoader loader, class<?>[] interfaces) {Map<class
<?>, boolean> interfaceset = new identityhashmap<> (interfaces.length); for (class<?> intf:interfaces) {/* * Verify that Class loader resolves the n
Ame of this * interface to the same Class object. * * class<?
> Interfaceclass = null;
try {interfaceclass = Class.forName (Intf.getname (), false, loader); catch (ClassNotFoundException e) {} if (Interfaceclass!= intf) {T
Hrow New IllegalArgumentException (intf + "is not visible from class loader");
} * * Verify that Class object actually represents a * interface. */if (!interfaceclass.isinterface ()) {throw new Illegalargumentexce
Ption (Interfaceclass.getname () + "is isn't an interface");
} * * Verify that this interface are not a duplicate. */if (Interfaceset.put (Interfaceclass, boolean.true)!= null) {throw new Illegalargu
Mentexception ( "Repeated interface:" + interfaceclass.getname ()); } 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 the * proxy class would be define D in the same package.
Verify that * All Non-public proxy interfaces are 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 interface
s, use Com.sun.proxy package proxypkg = Reflectutil.proxy_package + ".";
}/* Choose a name for the proxy class to generate.
*/Long num = Nextuniquenumber.getandincrement ();
String proxyname = proxypkg + proxyclassnameprefix + num;
* * Generate the specified proxy class. * * byte[] proxyclassfile = Proxygenerator.generateproxyclass (Proxyname, interfaces, Accessfla
GS); try {return DefineClass0 (loader, proxyname, proxyclassfile, 0, PROXYC
Lassfile.length); catch (CLASSFORmaterror e) {/* A classformaterror here means this (barring bugs in the * Proxy class generation code) there is some other * invalid aspect of the arguments to the
Proxy * Class creation (such as virtual machine limitations * exceeded).
* * Throw new IllegalArgumentException (e.tostring ());
}
}
}
The annotation for this class says that the proxy class is generated using the factory method through the specified ClassLoader and array of interfaces. And the name of this proxy class is:
The prefix of Proxy class is "$Proxy", the
private static final String Proxyclassnameprefix = "$Proxy";
Long num = Nextuniquenumber.getandincrement ();
String proxyname = proxypkg + Proxyclassnameprefix + nu
Therefore, the dynamically generated proxy class name is the package name + $Proxy +id ordinal number .
The generated process, the core code is as follows:
byte[] Proxyclassfile = Proxygenerator.generateproxyclass (
proxyname, interfaces, accessflags);
return DefineClass0 (loader, proxyname,
proxyclassfile, 0, Proxyclassfile.length)
These two methods, I did not continue to track down, DEFINECLASS0 () even a native method. All we need to know is to create a proxy dynamically.
Now we need to do some validation, I want to check the dynamically generated proxy class name is not the package name + $Proxy +id ordinal number .
public class Test {public static void main (string[] args) {//TODO auto-generated
Method stub Maotaijiu Maotaijiu = new Maotaijiu ();
Wuliangye Wu = new Wuliangye ();
Furongwang fu = new Furongwang ();
Invocationhandler jingxiao1 = new Guitaia (MAOTAIJIU);
Invocationhandler Jingxiao2 = new Guitaia (WU);
Invocationhandler Jingxiao3 = new Guitaia (FU); Sellwine dynamicproxy = (sellwine) proxy.newproxyinstance (MaotaiJiu.class.getClassLoader (), Maotaijiu.clas
S.getinterfaces (), JINGXIAO1); Sellwine dynamicProxy1 = (sellwine) proxy.newproxyinstance (MaotaiJiu.class.getClassLoader (), MAOTAIJIU.CLA
Ss.getinterfaces (), JINGXIAO2);
Dynamicproxy.mainjiu (); DynamicProxy1