Popular understanding of Java dynamic proxy

Introduction to Proxy Mode

The proxy mode is a commonly used design mode. Its function is to provide additional access methods for the target object, and to extend the additional functions of the target object without modifying the target object, such as counting execution time, printing logs, etc.

There are two proxy modes: static proxy and dynamic proxy.

Requirement: If you do not want to change the original code, and record the execution time of the user save method. The sample code is as follows:

interface

public interface UserService {
    public void saveUser(User user);
}

Implementation class

public class UserServiceImpl implements UserService {

    private UserDao userDaoImpl;

    @Override
    public void saveUser(User user) {
        userDaoImpl.save(user); 
    }
    ...
}

Static proxy implementation

A static proxy is generated before the program runs. Generally speaking, the proxy class and the target class implement the same interface or have the same parent class

proxy class

public class UserServiceProxyImpl implements UserService {

    private UserService UserServiceImpl;//代理类持有一个委托类的对象引用

    public UserServiceProxyImpl(UserService UserServiceImpl) {
        this.UserServiceImpl = UserServiceImpl;
    }

    @Override
    public void saveUser(User user) {
        long startTime = System.currentTimeMillis();
        System.out.println("开始记录时间");
        delegate.dealTask(taskName);    // 将请求分派给委托类处理
        long endTime = System.currentTimeMillis();
        System.out.println("保存用户信息方法耗时" + (endTime-startTime) + "毫秒");
    }
}

Static factory class that produces proxy objects

public class UserServiceProxyFactory {
    public static UserService getInstance() {
        return new UserServiceProxyImpl(new UserServiceImpl());
    }
}

client

public class ClientTest {
    public static void main(String[] args) {
        UserService UserServiceProxy = UserServiceProxyFactory.getInstance();
        UserServiceProxy.saveUser(new User("1","张三"));
    }
}

operation result

开始记录时间
保存用户信息方法耗时0.01毫秒

Advantages and disadvantages of static proxy:

advantage:

1. The business class UserServiceImpl only needs to pay attention to the business logic itself, which ensures the reusability of the business class.

2. There is no direct dependency between the client Client and the business class UserServiceImpl, which shields the specific implementation for the client.

shortcoming:

1. An interface of a proxy object only serves objects of one interface type, and a static proxy cannot be used when the program scale is slightly larger.

2. If a method is added to the interface, in addition to all implementing classes need to implement this method, all proxy classes also need to implement this method. Increased complexity of code maintenance

Dynamic proxy implementation

A dynamic proxy is a proxy object that is dynamically generated inside the JVM during the running process of the program. The proxy class can implement proxy for various types.

There are two types of dynamic proxies: JDK dynamic proxies and CGLIB dynamic proxies.

JDK dynamic proxy

JDK dynamic proxy needs to declare an intermediate class between the proxy class and the target class. This intermediate class needs to implement an interface InvocationHandler in jdk. The source code is as follows:

package java.lang.reflect;
public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

Intermediate class that produces proxy objects

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

public class JDKProxy implements InvocationHandler {

    private Object target; //持有目标对象的引用
    public JDKProxy(Object target){
        this.target = target;
    }

    //创建代理对象
    public Object createProxy(){
         //1.得到目标对象的classloader
        ClassLoader classLoader = target.getClass().getClassLoader();
         //2.得到目标对象的实现接口的class[]
        Class<?>[] interfaces = target.getClass().getInterfaces();

        //3.第三个参数需要一个实现InvocationHandler接口的对象
        //3-1.第一种写法,让当前类实现InvocationHandler,第三个参数写this
        return Proxy.newProxyInstance(classLoader, interfaces, this);

        //3-2.第二种写法,第三个参数用匿名内部类的形式,先注释掉
        /*return Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                long startTime = System.currentTimeMillis();
                System.out.println("开始记录时间");
                Object ret = method.invoke(target, args);   //执行目标对象方法,此处写target如果写proxy会死循环直到内存溢出
                long endTime = System.currentTimeMillis();
                System.out.println("保存用户信息方法耗时" + (endTime-startTime) + "毫秒");
                return ret;
            }
        });*/
    }
    /* 
    在代理实例上执行目标对象的方法 
    参数1 就是代理对象,一般不使用
    参数2 它调用的方法的Method对象
    参数3 调用的方法的参数
    */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long startTime = System.currentTimeMillis();
        System.out.println("开始记录时间");
        Object ret = method.invoke(target, args);//执行目标对象方法,此处写target如果写proxy会死循环直到内存溢出
        long endTime = System.currentTimeMillis();
        System.out.println("保存用户信息方法耗时" + (endTime-startTime) + "毫秒");
        return ret;
    }

}

client

public class ProxyTest {
    @Test
    public void test1() {
        UserService userService = new UserServiceImpl();    //1.创建目标对象
        JDKProxy factory = new JDKProxy(userService);   // 2.通过JKDProxy完成代理对象创建
        UserService userServiceProxy = (UserService)factory.createProxy();
        userServiceProxy.saveUser(new User("1","张三"));
    }
}

In the JDK dynamic proxy, there are two points to pay attention to:

1. Proxy.newProxyInstance(classLoader, interfaces, this); The bottom layer is how to create the proxy object

2. When is the invoke method executed, and who will call this method

<font color=red>Analysis 1>></font>How to generate proxy objects:

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
    throws IllegalArgumentException
    {
    if (h == null) {
        throw new NullPointerException();
    }
    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }
    /*
    * Look up or generate the designated proxy class.
    */
    Class<?> cl = getProxyClass0(loader, intfs);
    /*
    * Invoke its constructor with the designated invocation handler.
    */
    try {
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
            // create proxy instance with doPrivilege as the proxy class may
            // implement non-public interfaces that requires a special permission
            return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                public Object run() {
                    return newInstance(cons, ih);
                }
            });
        } else {
            return newInstance(cons, ih);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString());
    }
}

//继续看newInstance方法
private static Object newInstance(Constructor<?> cons, InvocationHandler h) {
    try {
        return cons.newInstance(new Object[] {h} );
    } catch (IllegalAccessException | InstantiationException e) {
        throw new InternalError(e.toString());
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString());
        }
    }
}

It can be seen from this that the proxy object is created by using reflection, first obtaining the constructor of the target object, and then generating the proxy object by constructing reflection.

<font color=red>Analysis 2>></font>When is the invoke method called:

We use a tool class to output the bytecode of the generated proxy object to disk, and then decompile to see what content the proxy object has

The tool classes are as follows:

public class ProxyGeneratorUtils {
    /**
        * 把代理类的字节码写到硬盘上
        * @param fileName 文件名
        * @param path 路径信息
        * @param clazz 目标类的接口数组
        */
    public static void writeProxyClassToHardDisk(String fileName, String path, Class<?>[] clazz) {
        byte[] classFile = ProxyGenerator.generateProxyClass(fileName, clazz);
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(path);
            out.write(classFile);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    //主方法
    public static void main(String[] args) {
        ProxyGeneratorUtils.writeProxyClassToHardDisk("$JDKProxy1","F:/$JDKProxy1.class",UserServiceImpl.class.getInterfaces());
    }
}

Run the main method to generate the proxy object bytecode file, decompile it with JD.exe and open it as follows

public final class $JDKProxy1 extends Proxy implements UserService {
    private static Method m1;
    private static Method m3;
    private static Method m0;
    private static Method m2;

    public $JDKProxy1(InvocationHandler arg0) throws  {
        super(arg0);
    }
    public final void saveUser(User arg0) throws  {
            try {
                super.h.invoke(this, m3, new Object[]{arg0});
            } catch (RuntimeException | Error arg2) {
                throw arg2;
            } catch (Throwable arg3) {
                throw new UndeclaredThrowableException(arg3);
            }
        }
    ...

Use a diagram to illustrate what happens inside the saveUser() method that calls the userServiceProxy proxy object

Popular understanding of Java dynamic proxy

CGLIB dynamic proxy

The cglib dynamic proxy also needs an intermediate class that generates proxy objects. This class needs to implement the MethodInterceptor interface. This interface is in the cglib package, which has been integrated by spring and is in the spring-core core package<br>

Intermediate class that produces proxy objects

import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class CglibProxy implements MethodInterceptor {

    private Object target; //持有目标对象的引用
    public CglibProxy(Object target){
        this.target = target;
    }
    //创建代理对象
    public Object createProxy(){
        Enhancer enhancer = new Enhancer(); //1.创建Enhancer
        enhancer.setSuperclass(target.getClass());  //2.传递目标对象的class
        enhancer.setCallback(this); //3.设置回调操作(相当于InvocationHanlder)
        return enhancer.create();
    }
    //相当于InvocationHanlder中的invoke
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        long startTime = System.currentTimeMillis();
        System.out.println("开始记录时间");
        Object ret = method.invoke(target, args);//执行目标对象方法,此处写target如果写proxy会死循环直到内存溢出
        long endTime = System.currentTimeMillis();
        System.out.println("保存用户信息方法耗时" + (endTime-startTime) + "毫秒");
        return ret;
    }
}

client

public class ProxyTest {
    @Test
    public void test1() {
        UserService userService = new UserServiceImpl();    //1.创建目标对象
        CglibProxy factory = new CglibProxy(customerService);   // 2.通过CglibProxy完成代理对象创建
        UserService userServiceProxy = (UserService)factory.createProxy();
        userServiceProxy.saveUser(new User("1","张三"));
    }
}

Generate proxy object bytecode, decompile with JD.exe as follows

import java.lang.reflect.Method;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class UserServiceImpl$$EnhancerByCGLIB$$1772a9ea extends UserServiceImpl implements Factory {
    ...
    public final void saveUser(User paramUser)
    {
        MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
        if (tmp4_1 == null){
            tmp4_1;
            CGLIB$BIND_CALLBACKS(this);
        }
        if (this.CGLIB$CALLBACK_0 != null) return;
        super.saveUser(paramUser);
    }
    ...
}

The difference between JDK dynamic proxy and CGLIB dynamic proxy:

1. JDK dynamic proxy is for interface proxy. The target class must implement the interface, and the generated proxy object will also implement the interface.

2. CGLIB proxy adopts inheritance, and the generated proxy object inherits from the target class, so the target class and target method cannot be modified with final.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324814715&siteId=291194637