在前面学习 设计模式 的时候,学习过 代理模式,如果没看过的可以点击链接看一下。
我们经常听到的是 没有中间商赚差价,代理 其实就相当于是 中间商,既然中间商会赚差价(额外增加开销),那么我们为什么要使用 代理 呢?
举个栗子,现在需要购买食物大米,你是直接去超市、小卖部、粮油店等 中间商 处买呢?还是直接下乡,去种植户里购买?如果直接购买的话,你不仅要知道你需要的是大米,还需要知道大米产自哪里、哪个季节收获、哪个区域售卖等等一系列问题,会额外增加消费者负担,而这些,其实可以交给 代理 搞定就行了,省心、省力。
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 步就搞定了,花费一些可以接受的代价,尽快满足需求,何乐而不为呢?
做过支付的同学应该深有体会,是对接支付宝、微信支付呢,还是分别对接几百家银行方便,相比不用多说了吧。
优点:
- 通过代理可以提供增值服务(扩展更多信息)
- 通过代理可以不需要知道具体实现细节
同时,缺点也很明显:
- 增加一个新的操作,需要增加新的接口,违反开闭原则,可扩展性差
- 虽然不需要知道具体实现细节,但是需要知道具体实现类,造成类的额外负担
- 还是需要知道具体实现类,导致类变得复杂
如果需要的种类成千上万,那么代理类就会庞大到无法维护,所以 动态代理 应用而生。
2. 动态代理
动态代理的核心是 JDK 提供的 Proxy 和 InvocationHandler。
结合 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 中也大量使用到动态代理,掌握好动态代理,能提升框架搭建的能力。