代理模式

89 阅读4分钟

静态代理

简介

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活 中常见的中介。比如你按照小卡片上的电话打过去寻求服务,一般不是由本人,可能是一个成年雄性接听电话,然 而真正做事情的可能是另一个小姐姐。

**定义:**为其他对象提供一种代理以控制对这个对象的访问。

目的:

  1. 通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性;
  2. 通过代理对象对访问进行控制

代理模式一般含有三个角色:

**抽象角色:**声明真实角色和代理角色的公共接口方法。

**真实角色:**需要实现抽象角色接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用。也就是真正的业 务逻辑在此。

**代理角色:**需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。将统一的流程控制都放到代理角色中处理!

实现

代理模式在生活中的例子,比较像跑腿。我本来需要去超市买菜,但是呢被窝里真舒服。于是找paotui服务,类似于代理,先到达超市,在超市购买我需要的东西,还能送货上门。

抽象角色

public interface IShop {
    void buy();
}

真实角色

public class Supermarket implements IShop {
    @Override
    public void buy() {
        System.out.println("Supermarket buy");
    }
}

代理角色

public class MeituanPaotui implements IShop {
    private IShop mark;
    MeituanPaotui(IShop mark){
        this.mark = mark;
    }
    @Override
    public void buy() {
        System.out.println("go to Supermarket");
        mark.buy();
        System.out.println("provide home delivery service");
    }
}

客户端调用

    public static void main(String []args) {
        IShop supermarket = new Supermarket();
        IShop meituan = new MeituanPaotui(supermarket);
        meituan.buy();
    }

问题

静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。一般来说,被代理对象和代理对象是一对一的关系,当然一个代理对象对应多个被代理对象也是可以的。 静态代理,一对一则会出现时静态代理对象量多、代码量大,从而导致代码复杂,可维护性差的问题,一对多则代理对象会出现扩展能力差的问题。

动态代理

简单使用

在运行时再创建代理类和其实例,因此显然效率更低。要完成这个场景,需要在运行期动态创建一个Class。JDK提供了 Proxy 来完成这件事情。基本使用如下:

    //抽象角色 
    interface Api { 
        void test(String a);
    }
    //真实角色 
    class ApiImpl implements Api{
        @Override
        public void test(String a) { 
            System.out.println("真实实现:" + a);
        }
    }
    
        ApiImpl imp = new ApiImpl();
        Api api = (Api) Proxy.newProxyInstance(getClass().getClassLoader(),
                new Class[]{Api.class},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Log.e(TAG,"invoke ...");
                        return method.invoke(imp,args);
                    }
                });
        api.test("ok");

原理分析

上面我们利用Proxy类的newProxyInstance方法创建了一个动态代理对象,查看该方法的源码,发现它只是封装了创建动态代理类的步骤(红色标准部分):

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        /*
         * 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 (!Modifier.isPublic(cl.getModifiers())) {
                cons.setAccessible(true);
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

在上面代码中,重要的几行如下: Class cl = getProxyClass0(loader, intfs);产生代理类。 final Constructor cons = cl.getConstructor(constructorParams);获取构造器。 return cons.newInstance(new Object[]{h}); 返回对应的代理类。

我们将这个生成的临时代理类,以文件形式输出出来。输出的方法如下:

        String name = Api.class.getName()+"$Proxy0";
        byte[] classFile = ProxyGenerator.generateProxyClass(name, new Class[]{Api.class});
        String path = "lib/" + name+".class";
        try(FileOutputStream fos = new FileOutputStream(path)) {
            fos.write(classFile);
            fos.flush();
            System.out.println("代理类class文件写入成功");
        } catch (Exception e) {
            System.out.println("写文件错误");
        }

这里注意的是,鄙人这边是android开发,直接用studio来写上述代码,结果发现ProxyGenerator找不到,最后发现是android的sdk里面没有对应的rt.jar,需要使用java环境并且是JDK1.8才能找到ProxyGenerator,这边环境的是IntelliJ IDEA。

输出的文件如下:

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

    public Api$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 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 void test(String 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");
            m3 = Class.forName("Api").getMethod("test", Class.forName("java.lang.String"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

如上动态代理类,执行test方法时会调用super.h.invoke(this, m3, new Object[]{var1})。也就是回调了InvocationHandler 的 public Object invoke(Object proxy, Method method, Object[] args) 的方法。 其中的m3是通过Class.forName("Api").getMethod("test", Class.forName("java.lang.String"))进行反射的Api的test方法。 剩下还有m0,m1,m2都是Object里的equals、toString、hashCode方法。

总结

静态代理,一对一则会出现时静态代理对象量多、代码量大,从而导致代码复杂,可维护性差的问题,一对多则代理对象会出现扩展能力差的问题。 动态代理,运行时生成代理类,效率会比静态代理低,但是减少了代码的复杂以及程序开发时间。