代理 | 动态代理 | 好像没有那么难

201 阅读5分钟

在前面学习 设计模式 的时候,学习过 代理模式,如果没看过的可以点击链接看一下。

我们经常听到的是 没有中间商赚差价代理 其实就相当于是 中间商,既然中间商会赚差价(额外增加开销),那么我们为什么要使用 代理 呢?

举个栗子,现在需要购买食物大米,你是直接去超市、小卖部、粮油店等 中间商 处买呢?还是直接下乡,去种植户里购买?如果直接购买的话,你不仅要知道你需要的是大米,还需要知道大米产自哪里、哪个季节收获、哪个区域售卖等等一系列问题,会额外增加消费者负担,而这些,其实可以交给 代理 搞定就行了,省心、省力。

1. 静态代理

静态代理

场景:购买大米。

Step. 1 定义食物接口

public interface IFood {
    void buyFood();
}

Step. 2 定义大米

public class RiceFood implements IFood{
    @Override
    public void buyFood() {
        System.out.println("buy food for rice");
    }
}

Step. 3 代理入场

/**
 * 食物代理类
 * 作用:用户只需要通过代理商购买食物,而不需要去农田里购买
 */
public class NormalFoodProxy implements IFood {

    private IFood food;

    public NormalFoodProxy(IFood food) {
        this.food = food; // 设置具体代理商
    }

    /**
     * 通过委托代理商购买商品
     * 好处:可以对商品进行加工处理
     */
    @Override
    public void buyFood() {
        beforeSale();
        food.buyFood();
        afterSale();
    }
    // 售前服务
    private void beforeSale() {
        System.out.println("代理商检查食物是否安全");
    }
    // 售后服务
    private void afterSale() {
        System.out.println("代理商提供送货上门服务");
    }

}

Step. 4 消费者购买

// 1. 找到代理商并告诉 ta,需要购买大米
NormalFoodProxy proxy = new new NormalFoodProxy(new RiceFood());
// 2. 购买大米
proxy.buyRice();

就是这么简单,2 步就搞定了,花费一些可以接受的代价,尽快满足需求,何乐而不为呢?

做过支付的同学应该深有体会,是对接支付宝、微信支付呢,还是分别对接几百家银行方便,相比不用多说了吧。

优点:

  1. 通过代理可以提供增值服务(扩展更多信息)
  2. 通过代理可以不需要知道具体实现细节

同时,缺点也很明显:

  1. 增加一个新的操作,需要增加新的接口,违反开闭原则,可扩展性差
  2. 虽然不需要知道具体实现细节,但是需要知道具体实现类,造成类的额外负担
  3. 还是需要知道具体实现类,导致类变得复杂

如果需要的种类成千上万,那么代理类就会庞大到无法维护,所以 动态代理 应用而生。

2. 动态代理

动态代理的核心是 JDK 提供的 ProxyInvocationHandler

结合 Proxy 注释介绍 Proxy 是什么:

Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.

简单翻译一下就是:Proxy 提供静态方法用来创建动态代理类和实例,所有的动态代理类都基于它。

看来,想要使用动态代理是绕不过 Proxy 的了。

Proxy 紧密结合的,还要有 ** InvocationHandler** 类。

InvocationHandler is the interface implemented by the invocation handler of a proxy instance.

Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

代码也很简单,就只有一个方法。

public interface InvocationHandler {
	public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

那么,Proxy 倒是是如何结合 InvocationHandler 的呢,接下来看一下源码:

public class Proxy implements java.io.Serializable {
	// 省略若干代码
	
	// parameter types of a proxy class constructor
	private static final Class<?>[] constructorParams = { InvocationHandler.class };
	// a cache of proxy classes
	protected InvocationHandler h;
	
	public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
		// 省略若干代码
		
		// 通过反射的方式将 InvocationHandler 对象 h 传递给实例化的类
		return cons.newInstance(new Object[]{h});
	}
}

然后看一下动态生成的类中 InvocationHandler 如何工作的。

public final void buyFood() throws  {
    try {
    	  // 调用 InvocationHandler 的 invoke 方法进行处理
    	  // 由此可见,自定义动态代理需要实现 InvocationHandler 的,要不然流程无法走通
        super.h.invoke(this, m3, (Object[])null);
    } catch (RuntimeException | Error var2) {
        throw var2;
    } catch (Throwable var3) {
        throw new UndeclaredThrowableException(var3);
    }
}

整体 JDK 实现流程如下图:

动态代理

3. 动态代理示例

由上面源码分析,自定义动态代理类需要 implements InvocationHandler,先定义一个代理。

// 除了 InvocationHandler,不需要实现额外接口
public class DynamicProxy implements InvocationHandler {

    private Object factory;

    public void setFactory(Object factory) {
        this.factory = factory;
    }

    public Object getFactory() {
        return factory;
    }

    /**
     * 通过 Proxy 获得动态代理对象
     *
     * @return
     */
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(factory.getClass().getClassLoader(),
                factory.getClass().getInterfaces(), this);
    }

    /**
     * 通过动态代理方法进行增强
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        doBefore();
        Object result = method.invoke(factory, args);
        doAfter();
        return result;
    }

    private void doBefore() {
        System.out.println("提供代理前服务");
    }

    private void doAfter() {
        System.out.println("提供代理后服务");
    }

}

测试代码如下:

public static void main(String[] args) {
    // 初始化动态代理类
    DynamicProxy proxy = new DynamicProxy();

    // 代理 RiceFood(此时动态代理类里面没有实现 IFood 接口)
    RiceFood riceFood = new RiceFood();
    proxy.setFactory(riceFood);
    // 获取动态代理类实例
    IFood riceProxy = (IFood) proxy.getProxyInstance();
    riceProxy.buyFood();
}

输出信息如下:

提供代理前服务
buy food for rice
提供代理后服务

总结:相较于静态代理,动态代理可扩展性更强,Retrofit 使用的就是 动态代理,Spring 中也大量使用到动态代理,掌握好动态代理,能提升框架搭建的能力。