源码角度来看代理Proxy类

2020-05-26  本文已影响0人  walker113

静态代理

要想了解动态代理,首先要知道静态代理,网上也有很多相关的文章,无非是说明相关的代码怎么写,等等,看完好像是明白的,但是到了自己使用的时候你会感觉到其实自己还是不怎么懂;

那下面开始说一下静态代理:

代码实例

嗯,看起来好像有点啰嗦,那就代码说话吧;

public interface IStudent {
    void doHomework();
    void learnEnglish();
}
public class MiddleSchoolStudent implements IStudent {
    @Override
    public void doHomework() {
        System.out.println("做中学作业");
    }
    @Override
    public void learnEnglish() {
        System.out.println("上中学的英语课");
    }
}

现在有一个需求,需要计算做中学作业以及上英语课所花费的时间是多少?

public class TimeProxy implements IStudent {
    private final IStudent student;
    public TimeProxy(IStudent student) {
        this.student = student;
    }
    @Override
    public void doHomework() {
        TimeUtil.start("doHomework");
        student.doHomework();
        TimeUtil.finish("doHomework");
    }
    @Override
    public void learnEnglish() {
        student.learnEnglish();
    }
}
public static void main(String[] args) {
    IStudent midStu = new MiddleSchoolStudent();
    TimeProxy timeProxy = new TimeProxy(midStu);
    timeProxy.doHomework();
}

动态代理

代码实例

使用的Java提供的Proxy类来创建动态代理;

static class Invocation implements InvocationHandler {
    private final IStudent student;

    public Invocation(IStudent student) {
        this.student = student;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        TimeUtil.start(method.getName());
        Object result = method.invoke(student, args);
        TimeUtil.finish(method.getName());
        return result;
    }
}
public static void main(String[] args) {
    IStudent midStu = new MiddleSchoolStudent();

    IStudent proxy = (IStudent) Proxy.newProxyInstance(
            IStudent.class.getClassLoader(),
            new Class[]{IStudent.class},
            new MidStudentProxy(midStu));
    proxy.doHomework();
}

断点追踪

  1. 首先我们点击进入newProxyInstance函数,在内部添加一个断点,然后使用debug模式运行;


    newProxyInstance.png
    image.png
  2. 跟踪下去,我们可以发现通过getProxyClass0() 方法,产生了一个$Proxy0类;


    Proxy0.png
  1. 可以看到,这里获取了$Proxy0类的构造器,并且传入的参数为InvocationHandler对象;
  1. 让我们继续追踪,proxy.doHomework()方法的调用过程


    image.png

$Proxy0 是如何产生的?

public static void main(String[] args) {
    IStudent midStu = new MiddleSchoolStudent();
    IStudent proxy = (IStudent) Proxy.newProxyInstance(
            IStudent.class.getClassLoader(),
            new Class[]{IStudent.class},
            new Invocation(midStu));

    proxy.doHomework();
    proxy.learnEnglish();

    byte[] bytes = ProxyGenerator.generateProxyClass(proxy.getClass().getSimpleName(), proxy.getClass().getInterfaces());
    try {
        // 保存到proxy.class 文件中
        FileOutputStream fileOutputStream = new FileOutputStream(new File("out/proxy.class"));
        fileOutputStream.write(bytes);
        fileOutputStream.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

总结

  1. 也就是说,当我们调用Proxy.newProxyInstance的时候,它会根据传递的接口,来声明对应接口的代理类 $Proxy0 ;
  2. 我们在上面的分析已经知道,h 是创建$Proxy0 传递的Invocation类,可以看到在各个方法内部都调用了invoke方法;那如何调用invoke这就可以解释的通了;
  3. Proxy0 的doHomework() 方法,实际上调用的是Proxy0代理类对象的doHomework()方法,此方法内部持有Invocation对象,那实际是调用Invocation对象的invoke() 方法,然后再调用 Invocation内部目标类对象的learnEnglish() 方法;
上一篇 下一篇

猜你喜欢

热点阅读