The article starts in "the blog Garden-Chen Shuyi", click to jump to the original text in layman's Java dynamic agent
Proxy mode is one of the most important types in design pattern, and design pattern is a very important knowledge point in programming, especially in the reconfiguration of business system, it has a pivotal position. The proxy mode can be divided into static agent and dynamic agent two types from the type.
Today I will use very simple and easy to understand the two types of dynamic agent, and then focus on the two implementation of dynamic agent (Java Dynamic agent and CGLib dynamic agent), finally in-depth analysis of the similarities and differences between the two implementation methods, finally, the application of dynamic agent in our surrounding framework.
Before we start, let's assume a scenario where there is a cake shop that uses cake machines to make cakes, and different kinds of cakes are made by different cake machines, so there are: fruit cake machines, chocolate cake machines, etc. This scenario is described in the Java language as follows:
//做蛋糕的机器public interface CakeMachine{ void makeCake();}//专门做水果蛋糕的机器class FruitCakeMachine implements CakeMachine{ public void makeCake() { System.out.println("Making a fruit cake..."); }}//专门做巧克力蛋糕的机器public class ChocolateCakeMachine implements CakeMachine{ public void makeCake() { System.out.printf("making a Chocolate Cake..."); }}//蛋糕店public class CakeShop { public static void main(String[] args) { new FruitCakeMachine().makeCake(); //making a Fruit Cake... new ChocolateCakeMachine().makeCake(); //making a Chocolate Cake... }}
The above code abstracts out a Cakemachine interface, a variety of cake machines (fruitcakemachine, chocolatecakemachine, etc.) to achieve the interface, the last cake shop (cakeshop) directly using the cake machine to make cakes.
An example of this is a true depiction of real-life scenarios. But the scene in life is often complex and changeable, assuming a customer at this time, he wants a fruit cake, but he particularly likes almonds, hoping to add a layer of almonds on the fruit cake. What should we do now?
Because our cake machine can only make fruit cake (programmed well), there is no way to make almond fruit cake. The simplest way is to directly modify the fruit Cake Machine program, make a cake to make almond fruit cake machine. The corresponding code changes in this way is also very simple, directly in the original code to modify, to create a special almond fruit cake machine is good, the modified Fruitcakemachien class should be like this:
//专门做水果蛋糕的机器,并且加上一层杏仁class FruitCakeMachine implements CakeMachine{ public void makeCake() { System.out.println("making a Fruit Cake..."); System.out.println("adding apricot..."); }}
While this approach has achieved our business needs. But think carefully, in real life if we meet such a demand, we can not because of a customer's special needs to modify a cake machine hardware program, so the cost is too high! And from a code implementation point of view, this way from the code is not very elegant, modified the original code . According to the idea of "closed to modify, open to extension" in the code circle, we should try to modify the original code as little as possible while trying to meet the new business requirements.
So what exactly should we do to make it more appropriate? We must make a cake directly with the fruit cake machine and then sprinkle it with a layer of almonds. This actually corresponds to even the mode of the proxy mode , in this business scenario, the waiter (agent) with the customer said no problem, can make fruit almond cake, so the waiter acted as an agent role, first let the fruit cake machine made a fruit cake, and then sprinkle a layer of almonds. In this case, the actual thing is the fruit cake machine, the waiter (the person who has the almond) just acts as an agent.
Let's try to implement the design of such a proxy model. What we need to do, in fact, is to design a proxy class (Fruitcakemachineproxy), this proxy class is equivalent to the person who sprinkle a layer of almond, then let the cake shop directly call can be the proxy class to achieve.
//水果蛋糕机代理public class FruitCakeMachineProxy implements CakeMachine{ private CakeMachine cakeMachine; public FruitCakeMachineProxy(CakeMachine cakeMachine) { this.cakeMachine = cakeMachine; } public void makeCake() { cakeMachine.makeCake(); System.out.println("adding apricot..."); }}//蛋糕店public class CakeShop { public static void main(String[] args) { FruitCakeMachine fruitCakeMachine = new FruitCakeMachine(); FruitCakeMachineProxy fruitCakeMachineProxy = new FruitCakeMachineProxy(fruitCakeMachine); fruitCakeMachineProxy.makeCake(); //making a Fruit Cake... adding apricot... }}
This business scenario is implemented through proxies so that we do not need to modify the original class to make the code more elegant and more scalable. If the next time the guests like the raisin fruit cake, then you can write another Currantcakemachineproxy class to sprinkle a layer of raisins, the original code will not be modified. The above-mentioned business scenario is the actual application of proxy mode, which is to say that this is a static proxy.
The complexity of the business scenario is often changeable, and if there is another guest who wants to sprinkle a layer of almonds on the chocolate cake, then we will not have to write another proxy class to let him do the same thing. If a guest wants to sprinkle a layer of almonds on a matcha cake, a guest wants to sprinkle a layer of almonds on the five-kernel cake ... So we're not going to write an infinite number of proxy classes?
In fact, in Java has already been designed for this situation, an interface, specifically to solve a similar problem, it is dynamic proxy--invocationhandler.
The difference between a dynamic proxy and a static proxy is that a static agent can only do some sort of proxy action (a cake machine) for a particular type (a cupcake), while a dynamic agent may perform some kind of proxy action (a sprinkle of almonds) on all types (all cake machines).
Next we do an abstract implementation of the code for this business scenario. First of all, we can see that the common denominator of this scenario is that we want to do "sprinkle a layer of almonds" on all kinds of cakes, so we do an almond dynamic agent (Apricothandler).
//杏仁动态代理public class ApricotHandler implements InvocationHandler{ private Object object; public ApricotHandler(Object object) { this.object = object; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = method.invoke(object, args); //调用真正的蛋糕机做蛋糕 System.out.println("adding apricot..."); return result; }}
After the agent of the almond is finished, we directly let the cake shop start:
public class CakeShop { public static void main(String[] args) { //水果蛋糕撒一层杏仁 CakeMachine fruitCakeMachine = new FruitCakeMachine(); ApricotHandler fruitCakeApricotHandler = new ApricotHandler(fruitCakeMachine); CakeMachine fruitCakeProxy = (CakeMachine) Proxy.newProxyInstance(fruitCakeMachine.getClass().getClassLoader(), fruitCakeMachine.getClass().getInterfaces(), fruitCakeApricotHandler); fruitCakeProxy.makeCake(); //巧克力蛋糕撒一层杏仁 CakeMachine chocolateCakeMachine = new ChocolateCakeMachine(); ApricotHandler chocolateCakeApricotHandler = new ApricotHandler(chocolateCakeMachine); CakeMachine chocolateCakeProxy = (CakeMachine) Proxy.newProxyInstance(chocolateCakeMachine.getClass().getClassLoader(), chocolateCakeMachine.getClass().getInterfaces(), chocolateCakeApricotHandler); chocolateCakeProxy.makeCake(); }}
The output is:
making a Fruit Cake...adding apricot...making a Chocolate Cake...adding apricot...
From the output we can see that this is consistent with the results we want. Dynamic proxies are more universal than static proxies and can reduce more repetitive code. Imagine this scenario. If you use static proxies, we need to write a proxy class for each type of cake machine (Fruitcakemachineproxy, Chocolatecakemachineproxy, Matchacakemachineproxy, etc.). But if we use a dynamic proxy, we just need to write a generic apricothandler, which can do all the work directly. Directly omitted to write Fruitcakemachineproxy, Chocolatecakemachineproxy, Matchacakemachineproxy Kung Fu, greatly improve the efficiency.
See here, you should know why the static agent, you need to have a dynamic agent it. static agents can only operate on one type of implementation (cake machine), and if you want to do the same for all types of implementations (all cake machines), then you have to be dynamic agents.
How do I use dynamic proxies?
Referring to the above example, we can know that to implement the dynamic agent needs to do two aspects of the work.
- You first need to create a new class, and this class must implement the Invocationhandler interface.
//杏仁动态代理public class ApricotHandler implements InvocationHandler{ private Object object; public ApricotHandler(Object object) { this.object = object; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = method.invoke(object, args); //调用真正的蛋糕机做蛋糕 System.out.println("adding apricot..."); return result; }}
- Use the Proxy.newproxyinstance () method to generate the proxy class when calling.
public class CakeShop { public static void main(String[] args) { //水果蛋糕撒一层杏仁 CakeMachine fruitCakeMachine = new FruitCakeMachine(); ApricotHandler fruitCakeApricotHandler = new ApricotHandler(fruitCakeMachine); CakeMachine fruitCakeProxy = (CakeMachine) Proxy.newProxyInstance(fruitCakeMachine.getClass().getClassLoader(), fruitCakeMachine.getClass().getInterfaces(), fruitCakeApricotHandler); fruitCakeProxy.makeCake(); }
- Finally, the associated method can be called directly using the generated proxy class.
Several implementation ways of dynamic agent
Dynamic agent refers to the concept of a design pattern, refers to the use of agents to do some common things, common applications have access to the system, log system, etc., are used to dynamic agents.
While the Java Dynamic agent is just one way to implement dynamic proxy, there is another way to implement dynamic agent, that is, CGLib(Code Generation Library).
Java dynamic agents can only be extended to the class that implements the interface, so careful friends will find that our code has an interface called Machinecake. CGLib, however, does not have this limitation, because CGLIB implements the proxy in the same way that it inherits the original class.
Let's give an example of how CGLIB implements dynamic proxies . Or the previous example: we want to make almond fruit cake, chocolate fruit cake, five-kernel chocolate cake, this time in the code is described in this way.
First we need to write an almond interceptor class, which can add almonds to a good cake.
public class ApricotInterceptor implements MethodInterceptor { public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { methodProxy.invokeSuper(o, objects); System.out.println("adding apricot..."); return o; }}
Then directly let the cake shop use the tools provided by CGLib to make almond fruit cakes:
public class CakeShop { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(FruitCakeMachine.class); enhancer.setCallback(new ApricotInterceptor()); FruitCakeMachine fruitCakeMachine = (FruitCakeMachine) enhancer.create(); fruitCakeMachine.makeCake(); }}
The above Enhancer.setsuperclass () setting requires an enhanced class, while Enhancer.setcallback () sets the interceptor that requires a callback, the class that implements the Methodinterceptor interface. Finally, the corresponding enhancement class is generated using Enhancer.create () and the final output is:
making a Fruit Cake...adding apricot...
The same as we expected. If you want to make an almond chocolate cake, then directly let the cake shop use Apricothandler to do one more, their difference is only the enhanced class passed in different.
public class CakeShop { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(ChocolateCakeMachine.class); enhancer.setCallback(new ApricotInterceptor()); ChocolateCakeMachine chocolateCakeMachine = (ChocolateCakeMachine) enhancer.create(); chocolateCakeMachine.makeCake(); }}
As you can see, the enhanced class passed in here is Chocolatecakemachine, not the previous fruitcakemachine.
Comparing the two implementations of the Java Dynamic Agent and the CGLib dynamic proxy, you will find that the Java Dynamic agent is suitable for the class proxy which has the interface abstraction, while the CGLIB is suitable for the class proxy without the interface abstraction.
The principle of Java dynamic Agent
From the above example, we can know that the Java dynamic agent is the portal from the Proxy.newinstance () method, then we start from this method to dissect the source side to understand its principle.
In fact, in this way, Java generates a proxy class (Apricothandler) instance that inherits the specified interface (cakemachine). From the source of the proxy.newinstance () we can see the first call to the GetProxyClass0 method, which returns a class instance object, which is actually the Apricothandler class object. It then obtains its constructor object and finally generates an instance of the Class object. In fact, the main thing here is the GetProxyClass0 () method, which dynamically generates the Apricothandler Class object. Let's dive into the GetProxyClass0 () method to see what's going on here.
The GetProxyClass0 () method first does some parameter validation, and then takes the Class object out of the Proxyclasscache parameter. In fact, Proxyclasscache is a Map object that caches all dynamically created Class objects. Note from the source code can be known that if the object taken from the Map is empty, then it calls proxyclassfactory to generate the corresponding Class object.
In the source code of the Proxyclassfactory class, the Proxygenerator.genrateproxyclass () method is finally called to generate the corresponding class bytecode file.
Here, we have the dynamic agent of the Java source code has been resolved, now the idea is very clear. Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
method simply means the following actions are performed:
- 1. Generate a byte code that implements all the interfaces in the parameter interfaces and inherits the proxy class, and then loads the proxy class with the ClassLoader in the parameter.
- 2. Use the proxy class parent constructor proxy (Invocationhandler h) to create an instance of the proxy class, passing in the subclass of our custom Invocationhandler.
- 3, return this proxy class instance, because we construct the proxy class to implement interfaces (that is, our program in the Fruitcakemachine.getclass (). Getinterfaces () All interfaces, so the returned proxy class can be strongly turned into The Machinecake type to invoke the method defined in the interface.
The principle of cglib dynamic agent
Because the JVM does not allow the original classes to be modified at run time, all the dynamics are implemented by new classes, and the Java dynamic agents mentioned above are no exception. Therefore, the principle of CGLib dynamic agent, in fact, is through the dynamic generation of proxy classes, and finally by the proxy class to complete the implementation of the operation.
For the implementation of CGLIB dynamic agent, I did not go into the source code, but by consulting the data to understand its approximate implementation principle.
- First, when we use the Enhancer.setsuperclass (Fruitcakemachine.class), we pass in a class that needs to be added, and CGLib generates a proxy class that inherits the class.
- Next, we passed the proxy class object through Enhancer.setcallback (new Apricotinterceptor ()), and CGLib implemented a static proxy by assembling the structure of two classes to achieve a specific purpose.
In the process of generating a new class, CGLib uses a thing called ASM, which operates on the Java class file and generates a new class file. If you are interested in the principle of CGLib, take a look at this article: from brother to Father: How does dynamic agent play in the folk?
Application of dynamic Agent
Dynamic proxies are very important in the code world, and many of the frameworks we have developed use this concept. All I know is that Spring AOP, Hibernate, and Struts use dynamic proxies.
- Spring AOP. one of the most important features of spring is AOP (Aspect oriented programming aspect-oriented programming), which uses Spring AOP to quickly implement common operations such as permission checking, security checking, and so on. The principles of spring AOP are implemented by dynamic proxies, which, by default, are implemented by the Java dynamic proxy, and are implemented using the CGLIB dynamic proxy when there is no corresponding interface for the class.
- Hibernate. Hibernate is a common ORM layer framework that is commonly used when fetching data: the Get () and load () methods, the difference being that the get () method takes the data directly, and the load () method delays loading. The proxy class is used to read the database until the user is really going to fetch the data.
- Struts. struts now knows that the interceptors in struts are known to have been used by struts, though they have been discarded because of too many bugs. Interceptors have very strong AOP features, and after a closer look you will find that Struts interceptors are actually implemented with dynamic proxies.
Summarize
We introduce the application of static agent and dynamic agent through different business scenarios of the cake shop, and then focus on the use of dynamic agent (Java Dynamic agent, CGLib dynamic Agent) and its implementation principle, in which a simple analysis is made for the source code of Java Dynamic Agent. Finally, we introduce the application of dynamic agents in practical programming (Spring AOP, Hibernate, Struts).
I hope this article will help you understand the dynamic agent better.
Above.
The article starts in "the blog Garden-Chen Shuyi", click to jump to the original text in layman's Java dynamic agent
Java Dynamic Agent in layman's