深入理解JDK动态代理

1. JDK动态代理Demo

1.1 Moveable接口

package jdkproxy;

/**
 * @author zhaobin11@baidu.com
 */
public interface Moveable {

    void move(int i);
}

1.2 Car实现类

package jdkproxy;

import java.util.Random;

/**
 * @author zhaobin11@baidu.com
 */
public class Car implements Moveable {

    @Override
    public void move(int i) {
        try {
            Thread.sleep(new Random().nextInt(1000));
            System.out.println("汽车行驶中..." + i);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

1.3 TimeHandler实现InvocationHandler

package jdkproxy;

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

/**
 * @author zhaobin11@baidu.com
 */
public class TimeHandler implements InvocationHandler {

    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.currentTimeMillis();
        System.out.println("汽车开始行驶...");
        method.invoke(target, args);
        System.out.println("汽车结束行驶,行驶时间为:" + (System.currentTimeMillis() - start) + "毫秒");
        return null;
    }
}

1.4 测试类Test

package jdkproxy;

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

/**
 * @author zhaobin11@baidu.com
 */
public class Test {

    public static void main(String[] args) {
        Car car = new Car();
        InvocationHandler h = new TimeHandler(car);

        /*
         * loader:类加载器
         * interfaces:目标对象实现的接口
         * h:InvocationHandler的实现类
         */
        Moveable m = (Moveable) Proxy.newProxyInstance(car.getClass().getClassLoader(), car.getClass().getInterfaces(), h);
        m.move(1);
        // ProxyGeneratorUtils.writeProxyClassToHardDisk("C:\\Users\\sakura\\Downloads\\study\\src\\jdkproxy\\$Proxy0.class");
    }
}

运行Test,可以看到

汽车开始行驶...
汽车行驶中...1
汽车结束行驶,行驶时间为:870毫秒

总结一下JDK动态代理的步骤:

  1. 需要定义一个接口,因为JDK的代理是基于接口实现的
  2. 接着定义一个类,实现步骤1中定义的接口
  3. 接着定义一个Handler类,实现InvocationHandler接口,并且把步骤2中定义的类的引用传进来,然后重写invoke方法就可以调用步骤2中类的方法并增强方法
  4. 根据被代理类的类加载器、被代理类实现的接口及步骤3中的实现类就可以生成代理类,代理类的类型就是被代理类的接口类型,然后就可以调用方法了

接下来我们看一下生成的代理类到底长什么样

1.5 ProxyGeneratorUtils类

package jdkproxy;

import sun.misc.ProxyGenerator;

import java.io.FileOutputStream;

/**
 * @author zhaobin11@baidu.com
 */
public class ProxyGeneratorUtils {

    public static void writeProxyClassToHardDisk(String path) {
        byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Car.class.getInterfaces());

        try (FileOutputStream out = new FileOutputStream(path)) {
            out.write(classFile);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

ProxyGenerator可以生成代理类文件
然后把1.4Test类中的注释打开再运行一下

// ProxyGeneratorUtils.writeProxyClassToHardDisk("C:\\Users\\sakura\\Downloads\\study\\src\\jdkproxy\\$Proxy0.class");

在C:\Users\sakura\Downloads\study\src\jdkproxy目录下就可以看到生成的$Proxy0代理类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import jdkproxy.Moveable;

// 1. 代理类$Proxy0继承自Proxy,实现了被代理类实现的接口Moveable接口
public final class $Proxy0 extends Proxy implements Moveable {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        // 2. $Proxy0在初始化调用构造函数时,会把实现InvocationHandler接口的类传进去,也就是初始化$Proxy0的父类Proxy里面的InvocationHandler
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    
    // 3. 调用代理类的move方法时,实际上是调用的实现InvocationHandler接口的类也就是TimeHandler的invoke方法,参数Proxy就是this即当前代理对象,参数method就是Moveable接口的方法move,参数args就是代理类调用move方法时传入的1
    public final void move(int var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            // 3. $Proxy0通过反射拿到了Moveable接口的方法move
            m3 = Class.forName("jdkproxy.Moveable").getMethod("move", Integer.TYPE);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

可以看到

  1. 代理类$Proxy0继承自Proxy,实现了被代理类实现的接口Moveable接口
  2. $Proxy0在初始化调用构造函数时,会把实现InvocationHandler接口的类传进去,也就是初始化$Proxy0的父类Proxy里面的InvocationHandler
  3. $Proxy0通过反射拿到了Moveable接口的方法move,然后当我们调用代理类的move方法时,实际上是调用的实现InvocationHandler接口的类也就是TimeHandler的invoke方法,参数Proxy就是this即当前代理对象,参数method就是Moveable接口的方法move,参数args就是代理类调用move方法时传入的1

2. 模拟实现JDK动态代理

这一部分主要是用反射模拟Proxy的newProxyInstance过程

2.1 Moveable接口

package jdkproxy2;

/**
 * @author zhaobin11@baidu.com
 */
public interface Moveable {

    void move();
}

2.2 Car实现类

package jdkproxy2;

import java.util.Random;

/**
 * @author zhaobin11@baidu.com
 */
public class Car implements Moveable {

    @Override
    public void move() {
        try {
            Thread.sleep(new Random().nextInt(1000));
            System.out.println("汽车行驶中...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

2.3 InvocationHandler接口

package jdkproxy2;

import java.lang.reflect.Method;

/**
 * @author zhaobin11@baidu.com
 */
public interface InvocationHandler {

    void invoke(Object o, Method m);
}

2.4 TimeHandler实现类

package jdkproxy2;

import java.lang.reflect.Method;

/**
 * @author zhaobin11@baidu.com
 */
public class TimeHandler implements InvocationHandler {

    private Object target;

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

    @Override
    public void invoke(Object proxy, Method method) {
        try {
            long start = System.currentTimeMillis();
            System.out.println("汽车开始行驶...");
            method.invoke(target);
            System.out.println("汽车结束行驶,行驶时间为:" + (System.currentTimeMillis() - start) + "毫秒");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2.5 Proxy类

package jdkproxy2;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * @author zhaobin11@baidu.com
 */
public class Proxy {

    public static Object newProxyInstance(Class infce, InvocationHandler h) throws Exception {
        String rt = "\r\n";
        String methodStr = "";

        for (Method m : infce.getMethods()) {
            methodStr +=
                    "    @Override" + rt +
                            "    public void " + m.getName() + "() {" + rt +
                            "       try{" + rt +
                            "           Method md=" + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt +
                            "           h.invoke(this,md);" + rt +
                            "       }catch(Exception e){" + rt +
                            "           e.printStackTrace();" + rt +
                            "       }" + rt +
                            "    }";
        }

        String str =
                "package jdkproxy2;" + rt +
                        "import java.lang.reflect.Method;" + rt +
                        "public class $Proxy0 implements " + infce.getName() + " {" + rt +
                        "    public $Proxy0(InvocationHandler h){" + rt +
                        "        this.h=h;" + rt +
                        "    }" + rt +
                        "    private InvocationHandler h;" + rt +
                        methodStr + rt +
                        "}";

        // 1. 产生代理类的java文件
        String fileName = System.getProperty("user.dir") + "/src/jdkproxy2/$Proxy0.java";
        File file = new File(fileName);
        FileUtils.writeStringToFile(file, str);

        // 2. load到内存
        Class clazz = ClassLoader.getSystemClassLoader().loadClass("jdkproxy2.$Proxy0");
        Constructor ctr = clazz.getConstructor(InvocationHandler.class);
        return ctr.newInstance(h);
    }
}

2.6 测试类Test

package jdkproxy2;

/**
 * @author zhaobin11@baidu.com
 */
public class Test {

    public static void main(String[] args) throws Exception {
        Car car = new Car();
        InvocationHandler h = new TimeHandler(car);
        Moveable m = (Moveable) Proxy.newProxyInstance(Moveable.class, h);
        m.move();
    }
}

运行Test,可以看到

汽车开始行驶...
汽车行驶中...
汽车结束行驶,行驶时间为:806毫秒

在C:\Users\sakura\Downloads\study\src\jdkproxy2目录下可以看到生成的$Proxy0代理类

package jdkproxy2;

import java.lang.reflect.Method;

public class $Proxy0 implements jdkproxy2.Moveable {

    public $Proxy0(InvocationHandler h) {
        this.h = h;
    }

    private InvocationHandler h;

    @Override
    public void move() {
        try {
            Method md = jdkproxy2.Moveable.class.getMethod("move");
            h.invoke(this, md);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

其实这个代理类就是用反射拼出来的,然后再反射调用

posted @ 2020-06-02 20:53  sakura1027  阅读(196)  评论(0编辑  收藏  举报