【Android每日一问】静态代理和动态代理,以及动态代理的好处

236 阅读3分钟

什么是代理?

代理,你就可以直接的理解为是我们所熟知的中介,中间商,中间平台等等。目的就是为我们服务达到目的。

以上可以看出,代理可以实现我们的方法增强(如购物的打折),比如一些常用的日志,缓存等。也可以实现方法拦截(如真假货替换),代理方法可以修改愿方法的参数以及返回值。

静态代理

静态代理就是最基本的代理方式,下面用一个例子来介绍一下:

  • 比如我们需要买东西,这时候需要提供一个购物的功能如下:
public interface Shopping {
    Object[] goShopping(int money);
}
  • 接着是我们的实现,我们要去亲自购物,如下:
public class ShoppingImpl implements Shopping {
    @Override
    public Object[] doShopping(int money) {
        return new Object[] { "鞋子", "衣服"};
    }
}
  • 如果我们不想自己买,这个时候就需要代理的出现:
public class ProxyShopping implements Shopping {

    Shopping base;

    ProxyShopping(Shopping base) {
        this.base = base;
    }

    @Override
    public Object[] doShopping(int money) {

        // 这里我们可以修改输入参数
        int moneyCount = money-500;

        System.out.println(String.format("花了%s块钱", moneyCount));

        // 帮忙买东西
        Object[] things = base.doShopping(moneyCount);

        // 修改返回值,鞋子替换成袜子
        if (things != null && things.length > 1) {
            things[0] = "袜子";
        }

        return things;
    }

由上可知,代理确实可以做到修改参数以及返回值,当然也可以做增强使用。

  • 静态代理的缺点: 传统的静态代理模式需要为每一个需要代理的类写一个代理类,这样会浪费大量的时间。

动态代理

如果用动态代理实现上面的功能又是如何的呢?如下:

public static void main(String[] args) {
    Shopping women = new ShoppingImpl();
    // 正常购物
    System.out.println(Arrays.toString(women.doShopping(100)
    // 动态代理实现
    women = (Shopping) Proxy.newProxyInstance(Shopping.class.getClassLoader(), 
            women.getClass().getInterfaces(), 
            new ShoppingHandler(womenSystem.out.println(Arrays.toString(women.doShopping(100)));
}

我们主要处理的类有ShoppingHandler

public class ShoppingHandler implements InvocationHandler {

    /**
     * 被代理的原始对象
     */
    Object base;

    public ShoppingHandler(Object base) {
        this.base = base;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        if ("doShopping".equals(method.getName())) {
            // 这里是代理Shopping接口的对象

            // 修改输入参数
            int money =  args[0];
            int moneyCount = (long) (money -500);

            // 帮忙买东西
            Object[] things = (Object[]) method.invoke(base, moneyCount);

            // 修改返回值,鞋子替换成袜子
            if (things != null && things.length > 1) {
                things[0] = "袜子";
            }

            return things;
        }

        if ("doSomething".equals(method.getName())) {
            // 可以代理别的,做些别的事情
            return null;
        }

        if ("doSomethingElse".equals(method.getName())) {
            // 做些别的事情
            return null;
        }

        return null;
    }
}

试想一下,我们如果有很多的类,做相同的事情,是不是用动态代理更加方便呢?

动态代理的优点

  • 只需要1个动态代理类就可以解决创建多个静态代理的问题,避免重复、多余代码

  • 更强的灵活性

动态代理的缺点

  • 效率低,相比静态代理中直接调用目标对象方法,动态代理则需要先通过Java反射机制 从而间接调用目标对象方法

  • 应用场景局限,因为Java的单继承特性(每个代理类都继承了Proxy类),即只能针对接口 创建代理类,不能针对类创建代理类。

在java的动态代理机制中,有两个重要的类或接口,一个是InvocationHandler接口、另一个则是 Proxy类,这个类和接口是实现我们动态代理所必须用到的。

InvocationHandler接口是给动态代理类实现的,负责处理被代理对象的操作的,而Proxy是用来创建动态代理类实例对象的,因为只有得到了这个对象我们才能调用那些需要代理的方法。