“This is the 28th day of my participation in the First Challenge 2022. For details: First Challenge 2022”


  • j3_liuliang
  • Learning process always hear agent agent, all today take a look to learn dynamic agent, hey hey!

1. What is the proxy mode


The proxy pattern is the structural pattern of objects. The proxy pattern provides a proxy for other objects to control access to that object. Simply put, in some cases, a client may not want or be able to directly reference another object, and a proxy object can act as an intermediary between the client and the target object.

For example, we plan to get married, but we don’t want to manage the details of the wedding. At this time, we find a wedding company and ask them to take care of the details of the wedding for us. This is the “agency model”. Since it is a proxy mode, there should be a proxy role and a real role. In this example, “we” is the real role, and “wedding Company” is the agent role. We plan to get married without having to worry about the details of the wedding, and that’s what the agency model gives us. Instead of writing everything down in the target object, we can hand it over to the proxy object agent to extend the target object.

As can be seen from the figure, proxy interface (Subject), proxy class (ProxySubject), and proxy class (RealSubject) form a “product” structure. Agents can be divided into static agents and dynamic agents according to their generation time.

Static proxy classes: created by programmers or automatically generated by a particular tool for source code and compiled. The.class file for the proxy class exists before the program runs. Dynamic proxy classes: dynamically created using reflection while the program is running.

Static proxies typically represent only one class, while dynamic proxies represent multiple implementation classes under an interface. Static agents know what to proide in advance, whereas dynamic agents don’t know what to proide until run time. The dynamic Proxy is the invoke method that implements the JDK’s InvocationHandler interface, but note that the Proxy is the interface, that is, your business class must implement the interface and get the Proxy object via the newProxyInstance in the Proxy.

There is also a dynamic proxy, CGLIB, which is a class that does not need a business class to inherit the interface and implements the proxy through a derived subclass. Classes are modified by dynamically modifying the bytecode at run time.

2. Static proxy


Static and dynamic proxies are illustrated with a mock requirement: the delegate class handles a long task, and the client class prints out the time spent performing the task. To solve this problem, record the time before and after the task execution. The time difference is the time consumed by the task execution.

1) Proxy interface

/** * proxy interface. Processes the task with the given name. * /
public interface Subject {
    /** * Executes the task with the given name. *@paramTaskName taskName */
    public void dealTask(String taskName);
}
Copy the code

2) Delegate class, handling business specifically.

/** * the class that actually performs the task, implements the proxy interface. * /
public class RealSubject implements Subject {

    /** * Executes the task with the given name. Here print out the task name and hibernate the 500ms simulation task executed for a long time * *@param taskName
     */
    @Override
    public void dealTask(String taskName) {
        System.out.println("In action:" + taskName);
        try {
            Thread.sleep(500);
        } catch(InterruptedException e) { e.printStackTrace(); }}}Copy the code

3) Static proxy classes

/** * proxy class that implements the proxy interface. * /
public class ProxySubject implements Subject {
    // The proxy class holds an object reference to the delegate class
    private Subject delegate;

    public ProxySubject(Subject delegate) {
        this.delegate = delegate;
    }

    /** * The request is assigned to the delegate class and the time before and after the task execution is recorded. The time difference is the processing time of the task **@param taskName
     */
    @Override
    public void dealTask(String taskName) {
        long stime = System.currentTimeMillis();
        // Assign the request to the delegate class for processing
        delegate.dealTask(taskName);
        long ftime = System.currentTimeMillis();
        System.out.println("Task Execution Time"+(ftime - stime)+"毫秒"); }}Copy the code

4) Generate static proxy factories

public class SubjectStaticFactory {
    // The client class calls this factory method to get the proxy object.
    // The client class does not know whether it is returning a proxy or delegate object.
    public static Subject getInstance(a){
        return new ProxySubject(newRealSubject()); }}Copy the code

5) Customer class

public class Client1 {

    public static void main(String[] args) {
        Subject proxy = SubjectStaticFactory.getInstance();
        proxy.dealTask("DBQueryTask"); }}Copy the code

6) the results

Executing a task: DBQueryTask The task execution takes 502 milliseconds

7) Advantages and disadvantages of static proxy class:

  • Business classes only need to focus on the business logic itself, ensuring the reuse of business classes. This is the common advantage of agency.

Disadvantages:

  • An interface to a proxy object serves only one type of object. If you have many methods to proxy, you must proxy for each of them. Static proxies cannot do the job when the program is a little larger.
  • If an interface adds a method, all proxy classes need to implement the method in addition to all implementation classes. Increases the complexity of code maintenance.
  • Serious violation of open and close principle (tears)
  • As the number of proxy interfaces increases, proxy classes grow larger and change whenever the proxy interface changes (Tear point)

Dynamic proxy


The source code for the dynamic proxy class is generated dynamically by the JVM during program execution, based on mechanisms such as reflection, so there are no bytecode files for the proxy class. The relationship between the proxy class and the delegate class is determined at runtime.

3.1) Take a look at the Java API closely associated with dynamic proxies.

3.1.1) Java. Lang. Reflect the Proxy

This is the parent of all dynamic proxy classes generated by the Java dynamic proxy mechanism, which provides a set of static methods to dynamically generate proxy classes and their objects for a set of interfaces.

1) Static method of Proxy class

// Method 1: This method is used to get the calling handler associated with the specified proxy object
static InvocationHandler getInvocationHandler(Object proxy)   
  
// Method 2: This method is used to get the class object associated with a dynamic proxy class that specifies a class loader and a set of interfaces
static Class getProxyClass(ClassLoader loader, Class[] interfaces)   
  
// Method 3: This method is used to determine whether the specified class object is a dynamic proxy class
static boolean isProxyClass(Class cl)   
  
// Method 4: This method is used to generate dynamic proxy class instances for the specified class loader, a set of interfaces, and the calling handler
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
Copy the code

3.1.2) Java. Lang. Reflect. InvocationHandler

This is the invocation handler interface, which defines a custom invoke method to focus on method calls on dynamic proxy class objects, where proxy access to delegate classes is typically implemented. A corresponding invocation handler object is specified each time a dynamic proxy class object is generated.

1) The core method of InvocationHandler

// This method is responsible for centrally handling all method calls on a dynamic proxy class. The first argument is both the proxy class instance and the second argument is the method object being called
// The third method is to call parameters. The invocation handler preprocesses or dispatches to the delegate class instance for reflection execution based on these three parameters
Object invoke(Object proxy, Method method, Object[] args)
Copy the code

3.1.3) Java. Lang. This

This is the classloader class, which is responsible for loading the class’s bytecode into the Java Virtual Machine (JVM) and defining class objects for it before the class can be used. The Proxy static method generates dynamic Proxy classes that also need to be loaded by the class loader to be used. The only difference is that the bytecode is generated dynamically by the JVM at runtime rather than preexisting in any.class file. A class loader object needs to be specified each time a dynamic proxy class object is generated

3.2) Dynamic proxy implementation steps

The specific steps are:

  1. Implement the InvocationHandler interface to create your own call handler
  2. Provide the Proxy class with a ClassLoader and an array of Proxy interface types to create a dynamic Proxy class
  3. Taking the calling processor type as parameter, the constructor of dynamic proxy class is obtained by reflection mechanism
  4. A dynamic proxy class object is created using the constructor of the dynamic proxy class, taking the call processor object as an argument

1) Realize dynamic proxy step by step

// InvocationHandlerImpl implements the InvocationHandler interface and dispatches method calls from the proxy class to the delegate class
// It usually contains a reference to the delegate class instance inside, which is used to actually perform the method call dispatched forward
InvocationHandler handler = newInvocationHandlerImpl(..) ;// Dynamically create a Proxy class object for a set of interfaces, including the Interface
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });   
  
// Get the constructor object from the generated class object by reflection
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });   
  
// Create a dynamic proxy class instance with the constructor object
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });  
Copy the code

The static method newProxyInstance of Proxy class encapsulates the last three steps of the above steps, simplifying the process of obtaining dynamic Proxy objects.

2) Simplified dynamic proxy implementation

// InvocationHandlerImpl implements the InvocationHandler interface and dispatches method calls from the proxy class to the delegate class
InvocationHandler handler = newInvocationHandlerImpl(..) ;// Create a dynamic Proxy class instance directly through Proxy
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader,   
     new Class[] { Interface.class },  handler ); 
Copy the code

3.3) Dynamic Proxy Implementation Example (JDK)

1) Create your own call handler

/** * The dynamic proxy class corresponds to the call handler class */
public class SubjectInvocationHandler implements InvocationHandler {

    // The proxy class holds an object reference to the delegate class
    private Object delegate;

    public SubjectInvocationHandler(Object delegate) {
        this.delegate = delegate;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long stime = System.currentTimeMillis();
        // Use reflection to assign the request to the delegate class. Method's invoke returns an Object as the result of Method execution.
        // Because the sample program does not return a value, return value processing is ignored here
        method.invoke(delegate, args);
        long ftime = System.currentTimeMillis();
        System.out.println("Task Execution Time"+(ftime - stime)+"毫秒");
        return null; }}Copy the code

2) Generate a factory for dynamic proxy objects. The factory method lists the steps for generating dynamic proxy class objects.

/** * a factory that generates dynamic proxy objects. */
public class DynProxyFactory {
    // The client class calls this factory method to get the proxy object.
    // The client class does not know whether it is returning a proxy or delegate object.
    public static Subject getInstance(a){
        Subject delegate = new RealSubject();
        InvocationHandler handler = new SubjectInvocationHandler(delegate);
        Subject proxy = null;
        proxy = (Subject) Proxy.newProxyInstance(
                delegate.getClass().getClassLoader(),
                delegate.getClass().getInterfaces(),
                handler);
        returnproxy; }}Copy the code

3) Dynamic proxy client class

public class Client {

    public static void main(String[] args) {

        Subject proxy = DynProxyFactory.getInstance();
        proxy.dealTask("DBQueryTask"); }}Copy the code

3.4) Characteristics of dynamic proxy mechanism

1) First, some characteristics of dynamically generated proxy classes themselves.

1) the package: If the propped interface is all public, it will be defined in the top-level package (i.e., the package path is empty). If the propped interface has non-public interfaces (since interfaces cannot be defined as protect or private, So it is the default package access level except public), then it will be defined in the package where the interface is (assuming that some non-public interface A in the com.ibm. developerWorks package is brokered, The package in which the newly generated proxy class resides is com.ibm.developerWorks). This is designed to ensure that the dynamic proxy class cannot be successfully defined and accessed due to package management problems.

2) Class modifiers: This proxy class has final and public modifiers, meaning that it can be accessed by all classes, but cannot be inherited again;

3) Class name: The format is “$ProxyN”, where N is an incrementing Arabic number representing the NTH dynamic Proxy class generated by Proxy. Note that N does not increase with each call to Proxy’s static method to create a dynamic Proxy class. Reason is that if the same set of interfaces (including interface are arranged in the order of the same) tries to create the dynamic proxy classes, it will return to a previously created wisely good proxy class class object, rather than to try to create a new proxy classes, that can save unnecessary code duplication, improve the efficiency of the proxy class to create.

4) Class inheritance relationship: The inheritance relationship of this class is shown as follows:

As you can see from the figure, Proxy class is its parent, and this rule applies to all dynamic Proxy classes created by Proxy. This class also implements the set of interfaces it proides, which is the root reason why it can be safely typed to one of the interfaces it proides.

2) Let’s look at some of the characteristics of proxy class instances.

Each instance is associated with a call handler object, which can be obtained through the static method getInvocationHandler provided by Proxy. When a proxy class instance calls a method declared in its proxy interface, these methods are eventually executed by the invoking processor’s Invoke method. In addition, it is worth noting that three methods in the root java.lang.Object class of the proxy class are also dispatched to the invoking processor’s Invoke method. They are hashCode, Equals, and toString for several possible reasons. First, these methods are public and non-final, and can be overridden by a proxy class. Secondly, these methods usually present some characteristic attributes of a class and have certain degree of differentiation. Therefore, in order to ensure the external consistency of the proxy class and the delegate class, these three methods should also be assigned to the delegate class for execution. When a group of interface agent has repeatedly promised method and this method is invoked, the proxy class always derive from the top interface method object is assigned to call processor, and no matter whether the proxy class instances in the interface (or a child of the inheritance in the interface interface) is in the form of external references, because inside the proxy class cannot distinguish between its current reference types.

Next, look at the characteristics of a proxied set of interfaces. First, be careful not to have duplicate interfaces to avoid compilation errors when generating dynamic proxy class code. Second, these interfaces must be visible to the class loader, otherwise the class loader will not be able to link them and the class definition will fail. Again, all non-public interfaces to be proxied must be in the same package, otherwise proxy class generation will also fail. Finally, the number of interfaces cannot exceed 65535, which is the limit set by the JVM.

Finally, take a look at the characteristics of exception handling. You can see from the method that calls the processor interface declaration that it can theoretically throw any type of exception, because all exceptions inherit from the Throwable interface, but is that true? The answer is no, because we must observe an inheritance principle: when a subclass overrides a parent class or implements a parent interface method, the exception thrown must be in the list of exceptions supported by the original method. So while calling the handler is theoretically possible, in practice it is often limited unless methods in the parent interface support Throwable exceptions. So what if an Invoke method does raise an exception that is not supported in the interface method declaration? Rest assured, Java dynamic proxy classes have designed a solution for us: it will be thrown UndeclaredThrowableException anomalies. This exception is of type RuntimeException, so it does not cause a compilation error. With the exception’s getCause method, you can also get the original unsupported exception object for error diagnosis.

3.5) Advantages and disadvantages of dynamic proxy

Advantages:

  • The biggest advantage of dynamic proxies compared to static proxies is that all methods declared in the interface are moved into a centralized method of the calling handler (InvocationHandler.invoke). In this way, when the number of interface methods is large, we can do flexible processing without the need for each method to be mediated like a static proxy. You can’t see it in this example, because the Invoke method has concrete peripheral businesses embedded within it (logging the time before and after the task is processed and calculating the time difference), which can be configured in practice like Spring AOP.

Fly in the ointment:

  • Yes, Proxy has been beautifully designed, but there is a small regret that it has never been able to escape the shackles of interface Proxy only because it was designed to do so. Recall the inheritance diagram for dynamically generated Proxy classes that are destined to have a common parent class called Proxy. Java’s inheritance mechanism prevents these dynamic proxy classes from implementing dynamic proxies to classes because multiple inheritance is inherently unworkable in Java.

There are many reasons why one can deny the need for a class proxy, but there are also some reasons why supporting a class dynamic proxy would be better. The division of interfaces and classes was not obvious until Java became so detailed. If you look at methods only in terms of how they are declared and whether they are defined, there is a hybrid called abstract class. Implementing dynamic proxies for abstract classes also has inherent value. In addition, there are legacy classes that will never be used for dynamic proxies because they do not implement any interfaces. Such a variety, have to say is a small regret.

3.6) Dynamic proxy based on interface

Interface:

public interface IUserMapper {}Copy the code

Proxy object:

public class ProxyObject<T> implements InvocationHandler {

    // It is possible to maintain a cache of objects abstracted from the methods of this interface
    private Class<T> proxyInterface;

    public ProxyObject(Class<T> proxyInterface) {
        this.proxyInterface = proxyInterface;
    }

    // Specific logic
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // Execute logic
        System.out.println("Come in.");
        System.out.println("Method name:"+method.getName());
        System.out.println(Arrays.toString(args));
        ArrayList<Object> objects = new ArrayList<>();
        objects.add("Hello");
        return objects;
    }

    // Return the proxy object
    public T getProxy(a) {
        return (T) Proxy.newProxyInstance(proxyInterface.getClassLoader(), new Class[]{proxyInterface}, this); }}Copy the code

Test it out:

@Test
public void testProxy(a) {
    ProxyObject<IUserMapper> iUserMapperProxyObject=new ProxyObject<>(IUserMapper.class);
    IUserMapper proxy = iUserMapperProxyObject.getProxy();
    List<User> all = proxy.findAll("1");
    System.out.println(all);
}
Copy the code

Change:

Use factory, singleton

public class ProxyFactory {

    private static ProxyObject proxyObject;

    public static ProxyObject getProxyObject(Class aClass) {
        if (proxyObject == null) {
            synchronized (ProxyFactory.class) {
                if (proxyObject == null) {
                    proxyObject = newProxyObject<>(aClass); }}}returnproxyObject; }}Copy the code

Testing:

@Test
public void testProxy(a) {
    ProxyObject proxyObject = ProxyFactory.getProxyObject(IUserMapper.class);
    IUserMapper proxy = (IUserMapper) proxyObject.getProxy();
    List<User> all = proxy.findAll("1");
    System.out.println(all);
}
Copy the code