JAVA Dynamic Proxy

1, Introduction to agent mode

        proxy pattern It is a design pattern that provides additional access to the target object, that is, access to the target object through the proxy object. In this way, it can provide additional function operations and expand the functions of the target object without modifying the original target object. In short, agent mode is to set up an intermediate agent to control the access to the original target object, so as to enhance the function of the original object and simplify the access mode. There is usually an association between the proxy class and the proxy class. The proxy class does not implement the service itself, but provides the service by calling the methods in the proxy class.

         For example, we often go to the railway station to buy tickets in our life, but if there are many people, it will be very crowded, so there is a selling point, and we can buy tickets from the selling point. This is the embodiment of the agent model. The agent selling point acts as the agent of the railway station object and provides the method of buying tickets.

2, Static proxy

         This proxy method requires that the proxy object and the target object implement the same interface, create an interface, and then create the proxy class to implement the interface and implement the abstract methods in the interface. Then create a proxy class and make it also implement this interface. In the proxy class, a reference to the proxy is held, then the method of the object is called in the proxy class method.

Example: static proxy implementation for saving user functions

  • Interface class: IUserDao
package com.proxy;

public interface IUserDao {
    public void save();
}
  • Target object: UserDao
package com.proxy;

public class UserDao implements IUserDao{

    @Override
    public void save() {
        System.out.println("Save data");
    }
}
  • Static proxy object: UserDapProxy   IUserDao interface needs to be implemented!
package com.proxy;

public class UserDaoProxy implements IUserDao{

    private IUserDao target;
    public UserDaoProxy(IUserDao target) {
        this.target = target;
    }
    
    @Override
    public void save() {
        System.out.println("Open transaction");//Extended additional functionality
        target.save();
        System.out.println("Commit transaction");
    }
}
  • Test class: TestProxy
package com.proxy;

import org.junit.Test;

public class StaticUserProxy {
    @Test
    public void testStaticProxy(){
        //Target object
        IUserDao target = new UserDao();
        //Proxy object
        UserDaoProxy proxy = new UserDaoProxy(target);
        proxy.save();
    }
}
  • Output results
Open transaction
 Save data
 Commit transaction

This proxy method requires that the proxy object and the target object implement the same interface.

advantage:

  • You can extend the function of the target object without modifying the target object.

Disadvantages:

  • Redundancy. Because the proxy object needs to implement an interface consistent with the target object, too many proxy classes will be generated.
  • Difficult to maintain. Once a method is added to the interface, both the target object and the proxy object must be modified.  

3, Dynamic agent

         Dynamic agents take advantage of JDK API , the proxy object is dynamically built in memory to realize the proxy function of the target object. Dynamic agent is also called JDK agent or interface agent.

The main differences between static agent and dynamic agent are:

  • The static proxy has been implemented at compile time. After compilation, the proxy class is an actual class file
  • The dynamic agent is generated dynamically at runtime, that is, there is no actual class file after compilation. Instead, the class bytecode is generated dynamically at runtime and loaded into the JVM

characteristic:
         The dynamic proxy object does not need to implement the interface, but the target object must implement the interface, otherwise the dynamic proxy cannot be used.

The main classes involved in generating proxy objects in JDK are:

static Object    newProxyInstance(ClassLoader loader,  //Specifies that the current target object uses a classloader

 Class<?>[] interfaces,    //The type of interface implemented by the target object
 InvocationHandler h      //Event handler
) 
//Returns a proxy class instance of the specified interface that can assign method calls to the specified call handler.
 Object    invoke(Object proxy, Method method, Object[] args) 
// Process method calls on proxy instances and return results.

Example: dynamic agent implementation for saving user functions:

  • Interface class: IUserDao
package com.proxy;

public interface IUserDao {
    public void save();
}
  • Target object: UserDao
package com.proxy;

public class UserDao implements IUserDao{

    @Override
    public void save() {
        System.out.println("Save data");
    }
}
  • Dynamic proxy object: UserProxyFactory
package com.proxy;

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

public class ProxyFactory {

    private Object target;// Maintain a target object

    public ProxyFactory(Object target) {
        this.target = target;
    }

    // Generate proxy object for target object
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                new InvocationHandler() {

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("Open transaction");

                        // Execute target object method
                        Object returnValue = method.invoke(target, args);

                        System.out.println("Commit transaction");
                        return null;
                    }
                });
    }
}
  • Test class: TestProxy
package com.proxy;

import org.junit.Test;

public class TestProxy {

    @Test
    public void testDynamicProxy (){
        IUserDao target = new UserDao();
        System.out.println(target.getClass());  //Output target object information
        IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
        System.out.println(proxy.getClass());  //Output proxy object information
        proxy.save();  //Execution agent method
    }
}
  • Output results
class com.proxy.UserDao
class com.sun.proxy.$Proxy4
 Open transaction
 Save data
 Commit transaction

4, cglib proxy

cglib   (Code Generation Library) is a third-party code generation class library, which dynamically generates a subclass object in memory at runtime, so as to expand the function of the target object.

cglib features

  • One limitation of dynamic proxy in JDK is that objects using dynamic proxy must implement one or more interfaces.
    If you want to proxy a class that does not implement an interface, you can use CGLIB implementation.
  • CGLIB is a powerful high-performance code generation package, which can extend Java classes and implement Java interfaces at run time.
    It is widely used by many AOP frameworks, such as Spring AOP and dynaop, to provide them with method interception.
  • The bottom layer of CGLIB package is to convert bytecode and generate new classes by using a small and fast bytecode processing framework ASM.
    Direct use of ASM is not encouraged because it requires you to be familiar with the internal structure of the JVM, including the format of class files and instruction sets.

The biggest difference between cglib and dynamic agent is

  • Objects that use dynamic proxies must implement one or more interfaces
  • The object using cglib proxy does not need to implement the interface, so that the proxy class does not invade.

Using cglib requires the introduction of jar package of cglib , if you already have the jar package of spring core, you don't need to introduce it, because spring contains cglib.

  • Maven coordinates of cglib
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.5</version>
</dependency>

Example: dynamic agent implementation for saving user functions

  • Target object: UserDao
package com.cglib;

public class UserDao{

    public void save() {
        System.out.println("Save data");
    }
}
  • Proxy objects: ProxyFactory
package com.cglib;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class ProxyFactory implements MethodInterceptor{

    private Object target;//Maintain a target object
    public ProxyFactory(Object target) {
        this.target = target;
    }
    
    //Generate proxy object for target object
    public Object getProxyInstance() {
        //Tool class
        Enhancer en = new Enhancer();
        //Set parent class
        en.setSuperclass(target.getClass());
        //Set callback function
        en.setCallback(this);
        //Create subclass object proxy
        return en.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Open transaction");
        // Method of executing target object
        Object returnValue = method.invoke(target, args);
        System.out.println("Close transaction");
        return null;
    }
}
  • Test class: TestProxy
package com.cglib;

import org.junit.Test;

public class TestProxy {

    @Test
    public void testCglibProxy(){
        //Target object
        UserDao target = new UserDao();
        System.out.println(target.getClass());
        //Proxy object
        UserDao proxy = (UserDao) new ProxyFactory(target).getProxyInstance();
        System.out.println(proxy.getClass());
        //Execute proxy object method
        proxy.save();
    }
}
  • Output results
class com.cglib.UserDao
class com.cglib.UserDao$$EnhancerByCGLIB$$552188b6
 Open transaction
 Save data
 Close transaction

5, Summary

  • The implementation of static proxy is relatively simple. As long as the proxy object wraps the target object, the enhanced function can be realized. However, static proxy can only serve one target object. If there are too many target objects, many proxy classes will be generated.
  • JDK dynamic proxy needs the target object to implement the business interface, and the proxy class only needs to implement the InvocationHandler interface.
  • The class generated by dynamic proxy is class com.sun.proxy. \ $proxy4, and the class generated by cglib proxy is class com.cglib.UserDao$$EnhancerByCGLIB$$552188b6.
  • The static agent generates class bytecode files during compilation, which can be used directly and with high efficiency.
  • The dynamic agent must implement the InvocationHandler interface. By reflecting the agent method, the system performance is consumed, but the number of agent classes can be reduced and the use is more flexible.
  • Cglib proxy does not need to implement the interface. It implements the proxy by generating class bytecode, which is slightly faster than reflection, and there is no performance problem. However, cglib inherits the target object and needs to rewrite the method, so the target object cannot be final.

Tags: Java

Posted on Sat, 25 Sep 2021 20:32:10 -0400 by Threehearts