51好读  ›  专栏  ›  爬蜥

代理模式与它在源码中的运用

爬蜥  · 掘金  ·  · 2019-01-27 14:16

正文

阅读 13

代理模式与它在源码中的运用

代理是指一个包装了真实访问对象的类,以便控制对真实类的访问

访问流程如下

public interface SubjectInterface {
    void hi();
}


public class RealSubject implements SubjectInterface {
    @Override
    public void hi() {
        System.out.print("hi");
    }
}

public class SubjectProxy implements SubjectInterface{
    private RealSubject r;
    public SubjectProxy() {
        r=new RealSubject();
    }

    @Override
    public void hi() {
        System.out.println("proxy");
        r.hi();
    }
}

public class Client {
    public static void main(String[] args) {
       SubjectInterface subject = new SubjectProxy();
        subject.hi();
    }
}
复制代码

此时RealSubject作为代理对象的一个属性字段,在运行之前就会生成RealSubject的字节码文件,这种方式也称作静态代理

动态代理

被代理的类在运行时动态生成的,编译的时候并没有生成RealSubject

使用JDK实现动态代理

jdk实现动态代理必须有实现接口InvocationHandler的处理类,用于执行被代理类的方法

public class SubjectInvocationHandler implements InvocationHandler {
    private Object myproxy;

    public SubjectInvocationHandler(Object proxy) {
        this.myproxy = proxy;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("invocation handler");
        method.invoke(myproxy,args);
        return null;
    }
}

复制代码

客户端通过使用 java.lang.reflect.Proxy 自行创建代理,然后调用目标方法即可

public class Client {
    public static void main(String[] args) {
    //classloader会被用来验证是否可以加载传入的接口,
     SubjectInterface proxy = (SubjectInterface) Proxy.newProxyInstance(SubjectInterface.class.getClassLoader()
     , new Class[]{SubjectInterface.class}
     , new SubjectInvocationHandler(new RealSubject()));
     proxy.hi();
 }
复制代码

访问流程如下

生成代理proxy class阶段

代理对象会在内部缓存,如果没有缓存则会由 ProxyClassFactory 新生成。
首先会做接口校验,比如是否可以从提供的classLoader获取接口

    Class<?> interfaceClass = null;
    try {
        interfaceClass = Class.forName(intf.getName(), false, loader);
    } catch (ClassNotFoundException e) {
    }
    if (interfaceClass != intf) {
        throw new IllegalArgumentException(
            intf + " is not visible from class loader");
    }
复制代码

验证完毕后,会读取接口的class文件使用的是 ProxyGenerator.generateProxyClass ,可以看到它会对整个的class文件的字节做读取

private byte[] generateClassFile() {
 ...
 dout.writeInt(0xCAFEBABE);
 ...
}
复制代码

最后调用native方法生成代理对象,并存入缓存

获取proxy的构造函数

构造函数指定的参数就是InvocationHandler

创建实例

调用构造函数,传入自定义的invocationHandler,自此生成了一个proxy实例,且实例本身会实现传入的接口,代码实例生成的对象如下所示

public final class $Proxy0 extends Proxy implements SubjectInterface {
    ...
       public final void hi() throws  {
        try {
           //这里的h即Proxy中的InvocationHandler,也就是用户自定义的InvocationHanlder
           //这个this对象代表的也就是 $Proxy0 本身
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    ...
}
复制代码






请到「今天看啥」查看全文