Understanding of spring dynamic proxy (java)

Understanding of java Dynamic Proxy

Proxy mode is one of the design modes and an important application in Java related frameworks.I am also a beginner, with my own opinions.Simply put, a proxy class that needs to be enhanced and a proxy class that is a real object will implement the same interface. The byte code file of the proxy class is generated dynamically when the jvm is running (described below), but the proxy class will inherit an extra Proxy class from Java, and the proxy class is responsible for preprocessing the trust for the proxy class (that is, the Java class that generates the real object).Information, enhanced information, and filtered information ultimately forward the enhanced information to the proxy class.However, when you think about it, who generated the proxy class? Therefore, you also need a class to dynamically generate the proxy class, which you write with an invocationhandler class in Java, which is used to enhance the methods in the proxy class, that is, who inherits the invocationhandler.Whoever implements the interface's method on the class that needs to be enhanced (the invoke method in the interface) and invokes the invoke method of the implementation class by calling the Generate Proxy Class Method of the Proxy Class Generator,This is one's own understanding. The dynamic generator class is just a Java class that generates dynamic proxy classes. It can't be referenced by other Java classes that you write (you can see if import imports your own classes in this class). It's all ready. It's just a test to see if it's possible to follow your own understanding.

1. Write code for dao and service first

dao implementation part code

public class EmployeeDAOImpl implements IEmployeeDAO {

    public void save(Employee emp) {
        System.out.println("Save Employee");
    }

    public void update(Employee emp) {
        System.out.println("Modify Employee");
    }
}

service implementation part code

public class EmployeeServiceImpl implements IEmployeeService {

    private IEmployeeDAO dao;

    public void setDao(IEmployeeDAO dao) {
        this.dao = dao;
    }

    public void save(Employee emp) {
        dao.save(emp);
        System.out.println("Save Successfully");
    }
}

2. Writing requires enhancing the way proxied objects are enhanced, such as transaction enhancements

//Analog Transaction Manager:
public class TransactionManager {

    public void begin() {
        System.out.println("Open Transaction");
    }

    public void commit() {
        System.out.println("Submit Transaction");
    }

    public void rollback() {
        System.out.println("Rollback transaction");
    }
}

3. Write a proxy class generator that generates proxy classes

The methods used in the following proxy and invocationhandler classes are introduced first

proxy class:

Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods. 

A Proxy class is a class used to dynamically generate a proxy object. I'll call it a dynamic proxy class generator.It provides many methods, but the one we use most is newProxyInstance:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException

The purpose of this method is to obtain an enhanced version of the proxy object, the proxy class, which receives three parameters, which represent the meaning of:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

loader:  One ClassLoader An object is one that defines which object it is classloader To load the proxy object to be generated,Class loaders for proxied objects are commonly used

interfaces:  One Interface An array of objects is the set of interfaces that I will provide to the objects I need to proxy. If I provide a set of interfaces to it, the proxy object claims to implement the interface.(polymorphic),
So I can call the methods in this set of interfaces, Popular is the interface implemented by the proxy class,Proxy classes also need this interface,Otherwise, how does it know which method you want to use? h:  One InvocationHandler Object, which dynamic proxy object I associate with when I call a method InvocationHandler On Object,That is, how to strengthen the proxy object

invocationhandler class:

InvocationHandler is the interface implemented by the invocation handler of a proxy instance. 

Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

Each dynamic proxy class must implement the InvocationHandler interface, and each instance of the proxy class is associated with a handler. When we call a method through a proxy object, the call to this method is forwarded to the invoke method of the InvocationHandler interface.It's abstract. As I understand it, when you use the dynamic proxy class generator, you generate the proxy object. But you only generate this proxy object. You also need to tell others how you can strengthen the proxy object.The only method for this interface, InvocationHandler, is the invoke method:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

The purpose of this method is to generate an enhanced version of the proxy object for the dynamic proxy class, that is, the proxy class method, how to strengthen it, it receives three parameters, which represent the meaning of the three parameters:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

proxy:  The real object being proxied for
method: To call a method of a real object Method object
args:  Parameters accepted when calling a method of a real object

Here's the big deal. I'm going to let the TransactionManagerAdvice class implement the InvocationHandler class directly. That's what I mentioned above to enhance the methods in the proxy class, using the invoke method.TransactionManagerAdvice is both a dynamic proxy class generator (using the newProxyInstance method in the proxy class) and a way to enhance the proxy object (that is, the real object) using the invoke method in the invocationhandler interface, where these two member variables are injected using the setter method in the xml-based DI.Therefore, a setter method for two member variables is also provided.

The dynamic proxy class generator is as follows:

//Transaction enhancements
public class TransactionManagerAdvice implements InvocationHandler {

    private Object target;//Real object(Who to Enhance)
    private TransactionManager txManager;//Transaction Manager(simulation)

    public void setTxManager(TransactionManager txManager) {
        this.txManager = txManager;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    //Create a proxy object
    public <T> T getProxyObject() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), //classloader,Class loaders that generally keep up with real objects
                target.getClass().getInterfaces(), //Interfaces implemented by real objects(JDK Dynamic proxy must require that real objects have interfaces)
                this);//How to Do Business Enhanced Objects,Who inherited it invocationhandler,Who is the object that needs to be enhanced,And the real object calling method will call
        //The implementation class's invoke Method, Your own understanding
    }

    //How to enhance the methods of real objects
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().startsWith("get") || method.getName().startsWith("list")) {
            return method.invoke(target, args);//Release
        }
        
        Object ret = proxy;
        txManager.begin();
        try {
            //---------------------------------------------------------------
            ret = method.invoke(target, args);//Call methods for real objects
            //---------------------------------------------------------------
            txManager.commit();
        } catch (Exception e) {
            e.printStackTrace();
            txManager.rollback();
        }
        return ret;
    }
}

4. Write test classes

@SpringJUnitConfig
//For loading the contents of the configuration file, look for it by default without parameters: test class name-context.xml
public class App { //Proxy class generator for generating enhanced proxy classes @Autowired private TransactionManagerAdvice advice; //Proxy Object:com.sun.proxy.$Proxy19 @Test void testSave() throws Exception { //Get Proxy Object IEmployeeService proxy = advice.getProxyObject(); proxy.save(new Employee()); } }

5. The contents of the configuration file are as follows (name of the configuration file: App-context.xml)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

  <!--because
    <bean id="employeeDAO" class="cn.wolfcode.dao.impl.EmployeeDAOImpl" />

    <bean id="transactionManager" class="cn.wolfcode.tx.TransactionManager" />

    <bean id="employeeService" class="cn.wolfcode.service.impl.EmployeeServiceImpl">
        <property name="dao" ref="employeeDAO" />
    </bean>

    <!-- Configure a Transaction Enhanced Class, This is the Proxy Class Generator -->
    <bean id="transactionManagerAdvice" class="cn.wolfcode.tx.TransactionManagerAdvice">
        <property name="target" ref="employeeService"/>
        <property name="txManager" ref="transactionManager"/>
    </bean>
</beans>

5. Run the save test with the following results:

 

 

 

 

 

 

 

The test passed and was successful.

Personal Notes: Extension of Understanding

See how dynamic agents operate at the bottom level

Start with the part generated by the dynamic proxy

Step 1: Using the static method newproxyinstance in the proxy class, the Java file of the proxy class is generated dynamically based on the parameters passed in, except that it will not be at compile time, but will go directly to run time and use the class loader to generate the byte code file.

Step 2: Enhance the method used by the invoke method of the proxy object, as the enhancements have been defined in the TransactionManagerAdvice class before, so the invoke method in this dynamically generated proxy class is a polymorphic attribute that calls the invoke method you have written

Step 3: Test Method

1. First of all, with the help of a class: the purpose of this class is to generate the byte code file of the proxy class, and store this byte code file under the package of the proxy class (that is, the byte code file of the proxy class). Previously, I was also curious why the proxy class should do this, which is also not mentioned before. Dynamic proxy does not generate Java files of the proxy class.Because we do not actually see the code implementation of the proxy class in the dynamic proxy process, and the proxy objects and proxy objects in the dynamic proxy process are implemented through the InvocationHandler interface with the newproxyinstance method in the class and proxy class, which is how to operate and why the proxy objects execute the method through the InvocationHandlerThe invoke method in is executed.The code to generate the dynamic proxy class is as follows

import java.io.FileOutputStream;

import cn.wolfcode.service.impl.EmployeeServiceImpl;
import sun.misc.ProxyGenerator; public class DynamicProxyClassGenerator {
    public static void main(String[] args) throws Exception {
        generateClassFile(EmployeeServiceImpl.class, "EmployeeServiceProxy");
    }

    //Generate byte code file for proxy class-->Java Decompilation tool-->Java file
    public static <T>void  generateClassFile(Class<T> targetClass, String proxyName) throws Exception {
        //Generate byte codes based on class information and proxy class name provided  
        byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, targetClass.getInterfaces());
        String path = targetClass.getResource(".").getPath();
        System.out.println(path);
        FileOutputStream out = null;
        //Keep on Hard Disk  
        out = new FileOutputStream(path + proxyName + ".class");
        out.write(classFile);
        out.close();
    }

}

2. After generating the byte code file, find the byte code file and generate the.java file with the jd-gui decompiling tool. Here is the code implementation of the dynamic proxy proxy class

public final class EmployeeServiceProxy extends Proxy implements IEmployeeService {
    private static final long serialVersionUID = 1L;
    private static Method method_equals;
    private static Method method_toString;
    private static Method method_hashCode;
    private static Method method_update;
    private static Method method_save;

    public EmployeeServiceProxy(InvocationHandler paramInvocationHandler) {
        super(paramInvocationHandler);
    }
    
    static {
        try {
            method_equals = Class.forName("java.lang.Object").getMethod("equals",new Class[] { Class.forName("java.lang.Object") });
            method_toString = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            method_hashCode = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            
            method_update = Class.forName("cn.wolfcode.service.IEmployeeService").getMethod("update",new Class[] { Class.forName("cn.wolfcode.domain.Employee") });
            method_save = Class.forName("cn.wolfcode.service.IEmployeeService").getMethod("save",new Class[] { Class.forName("cn.wolfcode.domain.Employee") });
        } catch (Exception e) {
        } 
    }

    public final boolean equals(Object paramObject) {
        try {
            return ((Boolean) this.h.invoke(this, method_equals, new Object[] { paramObject })).booleanValue();
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    

    public final String toString() {
        try {
            return (String) this.h.invoke(this, method_toString, null);
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    public final int hashCode() {
        try {
            return ((Integer) this.h.invoke(this, method_hashCode, null)).intValue();
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    public final void save(Employee paramEmployee) {
        try {
            this.h.invoke(this, method_save, new Object[] { paramEmployee });//Polymorphic call, using invoke method in TransactionManagerAdvice
            return;
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }
    
    public final void update(Employee paramEmployee) {
        try {
            this.h.invoke(this, method_update, new Object[] { paramEmployee });//Polymorphic call
            return;
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }
}

The invoke method in the proxy class generator above is marked red

3. Finally, the proxy class object is obtained through the proxy class generator of the test method, and the save method is called for the test, which is successful.

summary

We can think of the implementation class of InvocationHandler as a mediation class, which means that the mediation class of the proxy class object holds a proxy object and calls the corresponding method of the proxy object in the invoke method.By aggregating references to the proxy object, external calls to invoke are ultimately turned into calls to the proxy object.When a proxy class invokes its own method, it invokes the invoke method of the mediation class object through the mediation class object it holds, thereby enabling the proxy to execute the method of the proxy object.That is, dynamic proxy implements specific proxy functions through a mediation class.

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Partial Reference: https://www.cnblogs.com/gonjan-blog/p/6685611.html

Tags: Java xml Spring jvm

Posted on Sat, 16 May 2020 20:43:47 -0700 by agravayne