设计模式之代理模式(通过字节码探究执行过程)

代理模式

代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。

1、静态代理

我们有一个接口Movable,实现该接口的类都是可移动的:

interface Movable{
    void move();
}

public class Car implements Movable {
    @Override
    public void move() {
        System.out.println("Car move...");
        //模拟移动的时间,随机10秒以内
        try {
            TimeUnit.SECONDS.sleep(new Random().nextInt(10));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class Bicycle implements Movable {
    @Override
    public void move() {
        System.out.println("Bicycle move...");
        //模拟移动的时间,随机10秒以内
        try {
            TimeUnit.SECONDS.sleep(new Random().nextInt(10));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

现在想要实现的功能是在不改动源码的情况下得到移动的时间,我们只能生成一个中间类,将Movale的类组合进去,这个类也实现Movale接口,但是move方法中在调用m之前之后完成相应的功能:

class MoveTime implements Movable{

    Movable m;

    public MoveTime(Movable m) {
        this.m = m;
    }

    @Override
    public void move() {
        long s = System.currentTimeMillis();
        m.move();
        long e = System.currentTimeMillis();
        System.out.println("move time: "+(e-s));
    }
}

main函数,当要得到Car的move时间时,new Car,当想得到Bicycle的move时间时,new Bicycle:

public class Main {
    public static void main(String[] args) {
        MoveTime c = new MoveTime(new Car());
        c.move();
    }
}

MoveTime是代理类,Car、Bicycle都是被代理的对象。

静态代理实现了在不改变原有代码的情况下,完成对已有功能的增强,所谓增强,就是在完成原有功能的基础上添加新的功能。

静态代理的缺点是一个代理类只能实现一类接口的代理,当我们要对除Movale之外的接口类代理时,需要自己再手写一个代理类,一类接口对应一个代理类,这样做十分麻烦,于是就有了动态代理。

2、动态代理

动态代理是静态代理的一种进化,静态代理的缺点已经很明显,动态代理解决了这个问题。

首先需要明确的是,动态代理解决的是代理类的问题,那它是怎么实现代理类的生成呢?先看代码:

//接口与Car类完全不动
interface Movable{
    void move();
}

public class Car implements Movable {
    @Override
    public void move() {
        System.out.println("Car move...");
        //模拟移动的时间,随机10秒以内
        try {
            TimeUnit.SECONDS.sleep(new Random().nextInt(10));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

主函数调用:

    public static void main(String[] args) {
        System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        Car car = new Car();
        Movable m = (Movable) Proxy.newProxyInstance
                (
                        Car.class.getClassLoader(),
                        Car.class.getInterfaces(),
                        new MoveTimeProxy(car)
                );
        m.move();
    }

Movable m = (Movable) Proxy.newProxyInstance(Car.class.getClassLoader(),Car.class.getInterfaces(),new MoveTimeProxy(car));

这句话是通过newProxyInstance方法生成一个代理类,其中的参数:

  • 指定用哪个类加载器加载(ClassLoader loader
  • 指定该代理类要实现哪个接口(Class<?>[] interfaces
  • 指定调用处理器。(InvocationHandler h

其中最主要的是InvocationHandler,这个类主要实现处理过程,正如静态代理中的计算时间等功能,都通过这个类来实现:

//声明一个实现InvocationHandler接口的类,并重写invoke方法。
public class MoveTimeProxy implements InvocationHandler {

    Object obj;

    public MoveTimeProxy(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long s = System.currentTimeMillis();
        Object result = method.invoke(obj,args);
        long e = System.currentTimeMillis();
        System.out.println("move time: "+(e-s));
        return result;
    }
}

主函数调用结果:

//Car move...
//move time: 5000

不过我们反过来再看主函数,也没有调用MoveTimeProxy中的invoke方法啊,在哪里调用的呢?

执行过程分析:

System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");这句话是将动态代理生成的类进行保存。当执行后会发现生成了一个$Proxy0.class的文件,这个文件就是动态代理中间生成的文件,我们通过IDEA的反编译可以看到这个类的全貌:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package proxy.dynamicsproxy;

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

final class $Proxy0 extends Proxy implements Movable {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        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 void move() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    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);
        }
    }

    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"));
            m3 = Class.forName("proxy.dynamicsproxy.Movable").getMethod("move");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

这个类继承自Proxy,实现了Movable接口。

而这个类的构造函数:

public $Proxy0(InvocationHandler var1) throws  {
    super(var1);
}

其中的参数是InvocationHandler,而我们自己写的处理类也是实现了InvocationHandler这个接口,所以这里的var1就是我们写的MoveTime类。我们再点进去super看一眼:

protected Proxy(InvocationHandler h) {
    Objects.requireNonNull(h);
    this.h = h;
}

此时将我们的MoveTime类对象已经传给了this.h。记住这里,下面会讲到。

再回来看.class文件,其中有move方法:

public final void move() throws  {
    try {
        super.h.invoke(this, m3, (Object[])null);
    } catch (RuntimeException | Error var2) {
        throw var2;
    } catch (Throwable var3) {
        throw new UndeclaredThrowableException(var3);
    }
}

这个方法中调用的是super.h.invoke,惊不惊喜,意不意外,super.h就是我们传入的MoveTime类对象,调用它的invoke,就是我们自己写的那个invoke方法

所以大致流程是:当我们调用动态代理生成的类的move方法时,实则调用的是$Proxy0这个对象的的move方法,而这个方法中负责调用我们自己写的处理函数,也就是我们自己写的invoke方法。

而这个类是怎么生成的呢?

采用java 的asm框架,直接对java字节码进行操作,从而生成一个新类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值