Dynamic agents you have to learn

Keywords: Java Spring jvm Mybatis

Dynamic agents you have to learn

Background of problem

As a senior single dog, Xiaoqiang has finally ushered in her own spring. Under his relentless pursuit, Xiao Li, a colleague, finally agreed to give him a chance to have a one month investigation period. If she performs well in this month, she will officially communicate with him, or else she will not be pestered.

Although Xiaoqiang's mother and child are single until now, they have not studied little about their preferences for girls. They also know that chasing girls is A gift. Girls like cosmetics, so they decide to send Xiaoli A lipstick first. Xiaoli may agree to be his girlfriend as soon as she is happy. But after A long night of tossing and turning, she didn't find A suitable one. Finally, she learned from her colleague Xiaohong that Xiaoli has always liked it Korea A company lipstick, but this brand of lipstick in China is very difficult to buy, this can be bad Xiaoqiang.

In order to love the beautiful girl, go through fire and water, so Xiaoqiang decided to go to South Korea in person to buy this lipstick. But seeing the price of the round-trip ticket, I can't help but feel reluctant to give up. I usually don't give up to change my shorts. This time, I really want to bleed a lot. But when she thought of receiving lipstick, she jumped into his arms happily and smiled like an aunt. Finally, Xiaoqiang decides to go to South Korea to buy lipstick

Lipstick factory interface:

public interface KouHongFactory {
    public void saleKouHong(String color);
}

Factory A producing Lipstick:

public class AaFactory implements KouHongFactory{
    @Override
    public void saleKouHong(String color) {
        System.out.println("What you bought is"+color+"Color lipstick");
    }
}

Xiaoqiang goes to the counter of factory A to buy:

public class XiaoQiang {
    public static void main(String[] args) {
        //Company A produces A lipstick that goddess likes
        KouHongFactory factory = new AaFactory();
        //Xiaoqiang takes a plane and comes to the factory after many hardships
        //Buy death Barbie lipstick
        factory.saleKouHong("Barbie powder of death");
    }
}

What are the problems?

Xiaoqiang went to South Korea to buy lipstick. Because he didn't know Korean, he went around a lot in South Korea to find the exclusive store of this lipstick. He really suffered a lot. In addition, the number of air tickets returned from abroad is several thousand, which also takes up a beautiful weekend. And when he came back, Xiaoqiang gave lipstick to Xiaoli directly. Because there was no exquisite package, there was less surprise and sense of ceremony, and the most important thing was that the color number was not Xiaoli's favorite. Xiaoli did not show how happy she was. Naturally, there was no hug or flavor in her imagination. In a word, the present didn't achieve the desired effect, it took time, effort and money.

Solution

Who let him be the immortal Xiaoqiang? A setback can't defeat him. He decides to give a gift and choose a mouth red number suitable for Xiaoli, but he really doesn't understand the color number. Just as Xiaoqiang is brushing her circle of friends gloomily, she suddenly sees the circle of friends of Xiaomei, a primary school classmate. Xiaomei married a rich second generation last year. Now she often goes abroad to play, and helps her friends to buy some cosmetics. This time, Xiaomei happened to be in Korea. She sent a circle of friends to ask if there were any friends who needed to buy on behalf of her. She also had three sexy photo portraits. Xiaoqiang's saliva flowed. She thought that Xiaomei was Xiaoqiang's dream girl at the beginning. When she thought of her goddess Xiaoli's good figure, she fought again. She contacted Xiaomei and asked her to buy a lipstick from company A. Told Xiaomei about the last delivery of lipstick, and was laughed at by Xiaomei. Xiaomei decided to help him choose a color number, and asked him to send a picture of Xiaoli's plain face, and choose a lipstick suitable for her according to her skin color.

New Xiaomei as an agent also inherits lipstick factory interface

//Proxy objects, and real objects inherit the same interface
public class XiaoMei implements KouHongFactory {

    //Xiaomei does not produce lipstick. All lipstick manufacturers should be included
    public KouHongFactory factory;

    public XiaoMei(KouHongFactory factory){
        super();
        this.factory = factory;
    }

    @Override
    public void saleKouHong(String color) {
        System.out.println("You want to buy"+color+"Color lipstick");
        //Pre enhanced, recommend a color number
        color = before();
        factory.saleKouHong(color);
        //Post enhancement
        after();
    }
    public String before(){
        String color = "Aunt red";
        System.out.println("The color number recommended for you is"+color);
        return color;
    }
    public void after(){
        System.out.println("It's packed beautifully for you. It's free of charge. It can be returned in seven days");
    }
}

Small strong class

package Proxy;

public class XiaoQiang {
    public static void main(String[] args) {
        //Company A produces A lipstick that goddess likes
        KouHongFactory factory = new AaFactory();
        //Xiaomei is the agent
        XiaoMei xiaoMei  = new XiaoMei(factory);
        //Xiaoqiang asks Xiaomei to help buy a lipstick in the color of Barbie powder
        xiaoMei.saleKouHong("Barbie powder of death");

    }
}

Operation result:

You want to buy death Barbie Pink Lipstick
 The color number recommended for you is aunt red
 You bought aunt's red lipstick
 It's packed beautifully for you. It's free of charge. It can be returned in seven days

It can be seen from the results that Xiaomei didn't buy the pink lipstick of Barbie, but bought a more suitable red lipstick of her aunt. Besides, we also provide exquisite packing and free mail service, which can be returned for seven days. It's a one-stop service, from pre purchase consultation to after-sales service. After receiving the express delivery, Xiaoqiang took the lipstick with exquisite packaging, asked Xiaoli out for dinner, and gave it to her during the meal. This time, the goddess finally smiled happily. Seeing the goddess's smile, Xiaoqiang even thought about the name of the child.

Mode explanation

Proxy mode definition: provides a proxy object for the target object, and the proxy object controls the access to the target object.

Objective:

  • Through the introduction of proxy object, we can access the target object indirectly to prevent the unnecessary complexity of the system caused by direct access to the target object.
  • Enhance the original business by proxy object.

As an agent, Xiaomei enhanced the business of company A, without modifying the code of company A, which is A non-invasive enhancement. The second point does not affect the client's call, that is, the parameters required by Xiaoqiang's purchase process remain unchanged.

New problems

Xiaoli has been enthusiastic about Xiaoqiang for a few days, and has begun to become aloof. She always replied to the conversation, and, uh, she wanted to eat out for a variety of reasons. When Xiao Qiang was in distress, she thought about whether to send some more gifts to her. Since the lipstick had already been delivered, she would send a box of foundation and a set of water milk. The goddess would love it.

So he contacted Xiaomei. This time, Xiaomei took a vacation in Japan and returned home in two days. He told Xiaomei the idea that he wanted to buy foundation and water milk, but Xiaomei did not buy these products at present. But in the plea of Xiao Qiang, he said what happiness in his next life depended on small beauty. In line with the principles of not only destroying ten temples and destroying a marriage, Xiao Mei promised to help him buy foundation and water milk.

Foundation interface:

public interface FenDiFactory {

    public void saleFenDi(String color);
}

B produces foundation and sells:

public class BbFactory implements FenDiFactory{

    @Override
    public void saleFenDi(String color) {
        System.out.println("What you bought is"+color+"Color foundation");
    }
}

What should Xiaomei do at this time? Continue to add implemented interfaces? Like the following, what if you want to represent the milk? Is it unreasonable to implement the interface next, which violates the opening and closing principle (open for extension and close for modification)

public class XiaoMei implements KouHongFactory,FenDiFactory{
}

At that time, Xiaomei was already busy, and needed to rush to Korea to help others to buy lipstick, and to go to Japan to help Xiao Qiang to buy foundation. So she planned to recruit several employees to run errands for her, some went to Korea to buy lipsticks, others went to Japan to buy foundation, don't forget that her husband was rich in the two generation, and some were money. It was done, so Xiaomei set up Xiaomei purchasing company. Now it can buy lipsticks, foundation, water milk and other cosmetics.

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class XiaoMeiCompany implements InvocationHandler {

    //The Object to be represented. In order to use the Object type in general, you can represent various companies
    private Object factory;

    public Object getFactory(){
        return factory;
    }

    public void setFactory(Object factory){
        this.factory = factory;
    }
    //Get objects of dynamic Proxy through Proxy
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(factory.getClass().getClassLoader(),factory.getClass().getInterfaces(),this);
    }
    //Enhance methods through dynamic proxy objects
    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        before();
        //In order to improve generality, reflection is used, because many methods may need to be enhanced. Method is the enhanced method
        Object ret = method.invoke(factory,objects);
        after();
        return ret;
    }

    public void before(){
        System.out.println("Hello, thank you for your consultation. What can I do for you");
    }
    public void after(){
        System.out.println("It's packed beautifully for you. It's free of charge. It can be returned in seven days");
    }
}

The Proxy class and InvocationHandler interface are used here, which needs to be briefly introduced as follows:

In java, the agent company is equivalent to the JVM. It can dynamically generate agent objects, which is equivalent to specifying an employee to serve a customer.

Proxy provides a static method to generate a dynamic proxy class and its instances. The static method is newProxyInstance. The source code is as follows:

@CallerSensitive
    public static Object newProxyInstance(ClassLoader var0, Class<?>[] var1, InvocationHandler var2) throws IllegalArgumentException {
        Objects.requireNonNull(var2);
        Class[] var3 = (Class[])var1.clone();
        SecurityManager var4 = System.getSecurityManager();
        if (var4 != null) {
            checkProxyAccess(Reflection.getCallerClass(), var0, var3);
        }
        Class var5 = getProxyClass0(var0, var3);
        try {
            if (var4 != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), var5);
            }
            final Constructor var6 = var5.getConstructor(constructorParams);
            if (!Modifier.isPublic(var5.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        var6.setAccessible(true);
                        return null;
                    }
                });
            }
            return var6.newInstance(var2);
            ...
    }

This method returns a proxy instance of the specified interface, which schedules method calls to the specified call handler.

InvocationHandler is an interface with an associated call handler for each proxy instance. When a method is called on a proxy instance, the method call is encoded and dispatched to the invoke method that calls the handler. The function of invoke is to process the method call on the proxy instance and return the result.

public interface InvocationHandler {
    Object invoke(Object var1, Method var2, Object[] var3) throws Throwable;
}

In short, the InvocationHandler interface is equivalent to a company's specification. The invoke method specifies what the company does. For example, in this example, in addition to its own business (purchasing goods on behalf of others), there are pre enhancement (consulting before buying) and post enhancement (post-sale service). These are the specifications of a company, which are formulated by Xiaomei Yes, all employees must comply.

Three parameters in newProxyInstance: factory.getClass().getClassLoader() indicates the class loader, factory.getClass().getInterfaces() indicates the specified interface implemented, and this represents the InvocationHandler.

The InvocationHandler is only responsible for enhancing business, and the Proxy is only responsible for creating objects. In this example, the Proxy is used to specify employees in the company. The InvocationHandler specifies what services the employees provide to customers (pre purchase consultation, purchasing on behalf of others, package mailing, after-sales, etc.). It also embodies the principle of single responsibility.

Xiao Qiang buys lipstick and foundation through Xiaomei company.

public class XiaoQiang {
    public static void main(String[] args) {
        //Company A produces A lipstick that goddess likes
        KouHongFactory afactory = new AaFactory();
        //B produces Foundation
        FenDiFactory bfactory = new BbFactory();
        //Xiaomei's purchasing agency
        XiaoMeiCompany company = new XiaoMeiCompany();
        //Acting for lipstick of company A
        company.setFactory(afactory);
        //Order employee 1 to buy lipstick for Xiaoqiang
        KouHongFactory staff1 = (KouHongFactory) company.getProxyInstance();
        staff1.saleKouHong("Aunt red");
        System.out.println("---------------");

        company.setFactory(bfactory);
        //Order No. 2 to help Xiao Qiang buy foundation.
        FenDiFactory staff2 = (FenDiFactory) company.getProxyInstance();
        staff2.saleFenDi("Ivory");
    }
}

The operation results are as follows:

Hello, thank you for your consultation. What can I do for you
 You bought aunt's red lipstick
 It's packed beautifully for you. It's free of charge. It can be returned in seven days
---------------
Hello, thank you for your consultation. What can I do for you
 You bought the ivory foundation.
It's packed beautifully for you. It's free of charge. It can be returned in seven days

The class diagram of the proxy object is as follows:

Principle of dynamic agent

Let's first look at the complete life cycle of a class:

As shown in the figure, in which stage is the dynamic proxy generated? It is in the compilation stage. Because the dynamic proxy object does not have a corresponding java source file, how is the bytecode generated? There are three ways to generate bytecode:

1. Compile java source files from hard disk.

2. Hot load from the network, such as tomacat.

3. From memory, such as dynamic agent.

How does the JVM generate bytecode in memory? To analyze the source code, the first entry is the Proxy.newProxyInstance we call


Click in to view the newProxyInstance method, as follows:

The red line part above is the key. The first part is to generate Java bytecode and Class objects, and the last two parts are to generate instance objects. The key point we need to understand is how to generate Java bytecode and Class objects. Click here to see the getProxyClass0 method:

Focus on the underlined part. Continue to check the function of this line of code. Click get to enter the source code:

Core method apply():

The first parameter received is the class loader, the second is an array of interfaces to be implemented, and then traverse these interfaces for some checks. The code in the check part is omitted, and then

Generate a sequence number, and generate the name of the Proxy Class in the form of package name + $Proxy + sequence number. Then generate java bytecode and Class object. The method of generating bytecode generateProxyClass

From the underlined part, we can see that the bytecode file with the suffix of. class has been generated.

Write a tool class, use the above method to generate the bytecode of the proxy object and save it, and view the java source code through Decompilation

Tool class for storing bytecode

public class ProxyUtils {

    public static void generateClassFile(Class clazz,String proxyName){
        //Generate bytecode based on class information and proxy class name
        byte[] classFile = ProxyGenerator.generateProxyClass(proxyName,
                new Class[]{clazz});
        String paths = clazz.getResource(".").getPath();
        System.out.println(paths);
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(paths+proxyName+".class");
            out.write(classFile);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Add the following two lines of code after the XiaoQiang class to call the above tool class:

ProxyUtils.generateClassFile(afactory.getClass(),staff1.getClass().getSimpleName());
ProxyUtils.generateClassFile(bfactory.getClass(),staff2.getClass().getSimpleName());

Finally, output the saved bytecode file path and enter the directory to view. As expected, there are Proxy1.class and Proxy1.class and Proxy1.class and Proxy2.class files.

Use the jad tool to decompile. Before decompiling, you need to change the names of Proxy1.class, Proxy1.class, Proxy1.class and Proxy2.class, which cannot contain the $sign. Otherwise, you cannot find this file. Use the following command to decompile:

View generated Proxy0.java

import Proxy.AaFactory;
import java.lang.reflect.*;

public final class $Proxy0 extends Proxy
    implements AaFactory
{

    public $Proxy0(InvocationHandler invocationhandler)
    {
        super(invocationhandler);
    }
    //Omit some code
    //Core business code
    public final void saleKouHong(String s)
    {
        try
        {
            super.h.invoke(this, m3, new Object[] {
                s
            });
            return;
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

//Omit some code
 
}

The most important business method is saleKouHong. It can be seen that it calls super.h.invoke(), super refers to the parent class Proxy, and the attribute h refers to the InvocationHandler. Our own XiaoMeiCompany implements the InvocationHandler interface and rewrites the invoke() method

All the final business code actually calls the Invoke() method in XiaoMeiCompany. In this method, we have enhanced the business (pre method and post method). This is the core principle that dynamic agents can enhance business.

Related development

Classification of agents:

1. Static agent
Static proxy means that the proxy class exists before compilation. This method is very inflexible, rarely used in actual programming, not expanded.

2. Dynamic agent

Interview questions about dynamic agents:

1. Principle of spring transaction annotation implementation

2. Why can MyBatis directly use the mapper interface to access the database

3. How is it implemented that mybatis can print log information such as sql statements when opening debugging mode? What design patterns are used in the implementation

Application of agent:

Add log facets, transaction features, cache agents, system performance monitoring, method interception, permission control, traffic control, load balancing, etc.

76 original articles published, 55 praised, 30000 visitors+
Private letter follow

Posted by tsabar on Wed, 12 Feb 2020 04:38:18 -0800