持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情
上一篇文章我们在“房东卖方,中介代理”的场景里应用了“静态代理”模式。这篇文章我们会使用一个具体场景来解释一下“动态代理”。
应用举例
我们继续使用“房产交易”的场景来举例。我们都知道,现在的中介不仅有卖方业务,还有租房、家政、维修、做二房东等业务,但是如果每次中介新增一个业务,我们就新增一个代理,这样可以会比较繁琐。我们就会想,如果能提供通用的代理方法就好了。而这种就要通过“动态代理”来实现了。
这里我们对带代理类做一下修改:
public class Proxy implements InvocationHandler {
private Object object;
public Proxy(Object object){
object = object;
}
/**
* 调用被代理的方法
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(this.obj, args);
return result;
}
}
上述代码中实现的InvocationHandler 接口,是JDK 提供的动态代理接口,它可以对被代理的方法提供代理。
其中invoke 方法是接口InvocationHandler 定义必须实现的,它完成对真实方法的调用。动态代理是根据被代理的接口生成所有的方法,也就是说给定一个接口,动态代理就会实现接口下所有的方法。通过 InvocationHandler接口, 所有方法都由该Handler来进行处理, 即所有被代理的方法都由 InvocationHandler接管实际的处理任务。
然后我们还是用之前的买房场景来举例,看看使用动态代理如何实现。
public static void main(String[] args){
BuyHouse buyHouse = new BuyHouseImpl();
//定义一个handler
InvocationHandler handler = new Proxy(buyHouse);
//获得类的class loader
ClassLoader cl = buyHouse.getClass().getClassLoader();
//动态产生一个代理者
BuyHouse proxy = (BuyHouse) Proxy.newProxyInstance(cl, new Class[]{BuyHouse.class}, handler);
proxy.buyHouse();
}
我们可以看到,在main 方法中用到了Proxy 这个类的方法,
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
这个方法中的参数的作用分别是:
- loder:类加载器
- interfaces:要用来代理的接口,如上面的BuyHouse 接口
- h:一个 InvocationHandler 对象
InvocationHandler 其实本身是一个接口,然而每个代理的实例都有一个与之关联的InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的InvocationHandler 实现类,并由它决定如何处理。我们来看InvocationHandler 的源码:
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
InvocationHandler 内部有且只有一个 invoke 方法,正是这个方法决定怎样处理代理传递过来的方法请求调用。因为Proxy 动态产生的代理会调用InvocationHandler 的实现类,所以InvocationHandler 才是实际的执行者。
总结
这里我们看一下静态代理和动态代理的区别和关系
- 静态代理比较简单,流程也比较清晰,代理类需要自己编写代码写成。
- 动态代理,代理类需要通过Proxy.newInstance() 方法生成,如果会使用Java 中的动态代理,这种也不难。
- JDK实现的代理中,不论是静态代理还是动态代理,其本质流程是大致相同的,即代理与被代理者都要实现两样接口。它们的实质是面向接口编程。而在Java 中动态代理的另一种实现方式:CGLib可以不需要接口。
其实动态代理模式在日常开发中用的并不多,实际是因为动态代理代码维护起来比较麻烦,同时理解起来相对也比较困难,性能也不是很好。所以我们只需要对动态代理做一个简单的了解即可。