#IT star is not a dream. Agent mode is so simple

If you don't understand dynamic agents, you can't understand the principles of many of the most popular frameworks, such as spring.

If you don't understand the offer of dynamic agent, you are likely to get involved.

To understand dynamic agent, we must first understand agent pattern

What is agent mode?

There is a printer class

public class Printer {
    public void print(){
        System.out.println("Print!");
    }
}

I want to record the log before printing?

The simplest way: directly add the function of logging in front of the function of printing.

public class Printer {
    public void print(){
        System.out.println("Log!");
        System.out.println("Print!");
    }
  }

It seems that there is no problem, but we have modified the source code of the printer, broken the object-oriented opening and closing principle, and may affect other functions. How to solve it? It's easy to think that since I can't modify the original code, let me create a new class.

public class LogPrinter extends Printer {
    public void print(){
        System.out.println("Log!");
        System.out.println("Print!");
    }
}

This class inherits the printer's class, rewrites the printer's print method, and provides the function of logging. It is good to use this class when you need a printer in the future. The problem seems to have been solved, and we can further optimize on the basis of this solution:

First, an interface is abstracted:

public interface IPrinter {
    void print();
}

The printer class implements this interface:

public class Printer implements IPrinter {
       public void print(){
       System.out.println("Print!");
    }
}

Creating a printer agent class also implements the interface. In the constructor, the printer object is passed in. When the printing method of the interface is implemented, the printing method of the printer object is called and the function of logging is added in front:

public class PrinterProxy implements IPrinter {
    private IPrinter printer;
    public PrinterProxy(){
        this.printer = new printer();
    }
    @Override
    public void print() {
        System.out.println("Log");
        printer.print();
    }
}

Try one:

public class Test {
    public static void main(String[] args) {
        PrinterProxy proxy = new PrinterProxy();
        proxy.print();
    }
}

The results are:

Log
Printing

After that, we can directly instantiate the PrinterProxy object to call its printing method, which is the static proxy mode. By abstracting out the interface, the program has higher scalability and flexibility.

Is static agent perfect?

Consider that if there are other methods in my printer class that also need the function of logging, I have to write the function of logging n times. Furthermore, if I still have a TV or refrigerator class and all the methods in it need to be added with the function of logging, there will be more repetitions.

What should I do?

Dynamic agent comes on stage:

If you don't want to write the logging function repeatedly, it's certainly not feasible to implement a proxy class for each interface. Can you let these proxy class objects generate automatically?

Jdk provides the invocationHandler interface and Proxy class, with which we can achieve the desired effect.

invocationHandler interface:

//Object proxy: the object being proxied
//Method method: method to call
//Object [] args: required parameter for method call
public interface InvocationHandler {
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

There is only one method invoke in the interface. This method is very important. I'll get familiar with it first and explain later.

Proxy class is used. It has a very important method, newProxyInstance:

//CLassLoader loader: CLassLoader of the proxy object
//Class <? > interfaces: all interfaces of the proxied class
//InvocationHandler h: object implementing the InvocationHandler interface
 public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

The Proxy object can be generated by calling the newProxyInstance method of Proxy

All ready dynamic agent mode is coming out:

The code of the interface IPrinter and the implementation class Printer of the interface are the same as before.

Implement a class to create a proxy object, which implements the InvocationHandler interface:

public class ProxyHandler implements InvocationHandler {
    private Object targetObject;//Represented object
/ / pass in the proxy object to obtain its classloader and implementation interface as parameters of the Proxy.newProxyInstance method.
    public  Object newProxyInstance(Object targetObject){
        this.targetObject = targetObject;
/ / targetObject.getClass().getClassLoader(): class loader of the proxy object
/ / targetObject.getClass().getInterfaces(): the implementation interface of the proxy object
/ / this is the current object. This object implements the InvocationHandler interface, so there is an invoke method. Through the invoke method, you can call the methods of the proxied object
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);
    }
/ / this method is called when the proxy object calls the method
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Log");
        return method.invoke(targetObject,args);
    }
}

The proxy object targetObject can be passed in through the method parameter:

public Object newProxyInstance(Object targetObject){
       this.targetObject=targetObject;

Let's focus on this Code:

return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);

The dynamic proxy object is created and returned by calling this code.

Method has three parameters:

First parameter:

targetObject.getClass().getClassLoader(): class loader of targetObject object.

Second parameter:

targetObject.getClass().getInterfaces(): all interfaces of targetObject object

The third parameter:

this: that is, the current object is the object of the class that implements the InvocationHandler interface, and its invoke method will be called when the method is called.

Let's look at this code again:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/ / here you can determine what function to perform by judging the method name
      System.out.println("Log");
/ / call the method of the proxy object
      return method.invoke(targetObject, args);
}

This method is the way to automatically call the method in the proxy class generated when it is invoked. We can see that the method of proxy being called in this method is method.invoke(targetObject, args).

We can add the required business logic here, such as logging before calling methods

It's time to witness the miracle:

public class Test {
   public static void main(String[] args){
   ProxyHandler proxyHandler=new ProxyHandler();
   IPrinter printer=(IPrinter) proxyHandler.newProxyInstance(new Printer());
   printer.print();
 }
}

Print results:

Log
Printing

When printer.print(); is executed, the invoke method will be called automatically. Many beginners don't understand why this method can be called. Recall that the proxy object was created through

return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(),this);

The third parameter of this method is the object that implements the InvocationHandler interface. The InvocationHandler interface has the invoke method. Now I have some ideas~

When the object being proxied is passed in as a parameter, any method in it can be executed. All method calls are completed through invoke. Do not need to deal with each method, dynamic agent is not very concise.

Definition of agent mode: agent mode provides a proxy object for an object, and the proxy object controls the reference to the original object. Generally speaking, agent mode is a common intermediary in our life. The difference between dynamic agent and static agent is that we need to manually implement the proxy class of the target object, while dynamic agent can be dynamic during operation Build agent class for.



Tags: Programming Spring JDK

Posted on Wed, 05 Feb 2020 03:31:05 -0800 by tjhilder