设计模式(四)——搞懂什么是代理模式

代理模式

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

代理模式的通用类图

上图中,Subject是一个抽象类或者接口,RealSubject是实现方法类,具体的业务执行,Proxy则是RealSubject的代理,直接和client接触的。git

代理模式能够在不修改被代理对象的基础上,经过扩展代理类,进行一些功能的附加与加强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。github

代理模式优势

  • 职责清晰
  • 高扩展,只要实现了接口,均可以用代理。
  • 智能化,动态代理。

分类

代码:GitHubshell

一、静态代理

以租房为例,咱们通常用租房软件、找中介或者找房东。这里的中介就是代理者。编程

首先定义一个提供了租房方法的接口。微信

public interface IRentHose {
    void rentHose();
}
复制代码

定义租房的实现类ide

public class RentHose implements IRentHose {
    @Override
    public void rentHose() {
        System.out.println("租了一间房子。。。");
    }
}
复制代码

我要租房,房源都在中介手中,因此找中介测试

public class IntermediaryProxy implements IRentHose {

    private IRentHose rentHose;

    public IntermediaryProxy(IRentHose irentHose){
        rentHose = irentHose;
    }

    @Override
    public void rentHose() {
        System.out.println("交中介费");
        rentHose.rentHose();
        System.out.println("中介负责维修管理");
    }
}
复制代码

这里中介也实现了租房的接口。this

再main方法中测试spa

public class Main {

    public static void main(String[] args){
        //定义租房
        IRentHose rentHose = new RentHose();
        //定义中介
        IRentHose intermediary = new IntermediaryProxy(rentHose);
        //中介租房
        intermediary.rentHose();
    }
}
复制代码

返回信息

交中介费
租了一间房子。。。
中介负责维修管理
复制代码

这就是静态代理,由于中介这个代理类已经事先写好了,只负责代理租房业务

二、强制代理

若是咱们直接找房东要租房,房东会说我把房子委托给中介了,你找中介去租吧。这样咱们就又要交一部分中介费了,真坑。

来看代码如何实现,定义一个租房接口,增长一个方法。

public interface IRentHose {
    void rentHose();
    IRentHose getProxy();
}
复制代码

这时中介的方法也稍微作一下修改

public class IntermediaryProxy implements IRentHose {

    private IRentHose rentHose;

    public IntermediaryProxy(IRentHose irentHose){
        rentHose = irentHose;
    }

    @Override
    public void rentHose() {
        rentHose.rentHose();
    }

    @Override
    public IRentHose getProxy() {
        return this;
    }
}
复制代码

其中的getProxy()方法返回中介的代理类对象

咱们再来看房东是如何实现租房:

public class LandLord implements IRentHose {

    private IRentHose iRentHose = null;

    @Override
    public void rentHose() {
        if (isProxy()){
            System.out.println("租了一间房子。。。");
        }else {
            System.out.println("请找中介");
        }
    }

    @Override
    public IRentHose getProxy() {
        iRentHose = new IntermediaryProxy(this);
        return iRentHose;
    }

    /** * 校验是不是代理访问 * @return */
    private boolean isProxy(){
        if(this.iRentHose == null){
            return false;
        }else{
            return true;
        }
    }
}
复制代码

房东的getProxy方法返回的是代理类,而后判断租房方法的调用者是不是中介,不是中介就不租房。

main方法测试:

public static void main(String[] args){

        IRentHose iRentHose = new LandLord();
        //租客找房东租房
        iRentHose.rentHose();
        //找中介租房
        IRentHose rentHose = iRentHose.getProxy();
        rentHose.rentHose();


    }

}
复制代码
请找中介
租了一间房子。。。
复制代码

看,这样就是强制你使用代理,若是不是代理就无法访问。

三、动态代理

咱们知道如今的中介不单单是有租房业务,同时还有卖房、家政、维修等得业务,只是咱们就不能对每个业务都增长一个代理,就要提供通用的代理方法,这就要经过动态代理来实现了。

中介的代理方法作了一下修改

public class IntermediaryProxy implements InvocationHandler {


    private Object obj;

    public IntermediaryProxy(Object object){
        obj = 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接管实际的处理任务。

这里增长一个卖房的业务,代码和租房代码相似。

main方法测试:

public static void main(String[] args){

    IRentHose rentHose = new RentHose();
    //定义一个handler
    InvocationHandler handler = new IntermediaryProxy(rentHose);
    //得到类的class loader
    ClassLoader cl = rentHose.getClass().getClassLoader();
    //动态产生一个代理者
    IRentHose proxy = (IRentHose) Proxy.newProxyInstance(cl, new Class[]{IRentHose.class}, handler);
    proxy.rentHose();

    ISellHose sellHose = new SellHose();
    InvocationHandler handler1 = new IntermediaryProxy(sellHose);
    ClassLoader classLoader = sellHose.getClass().getClassLoader();
    ISellHose proxy1 = (ISellHose) Proxy.newProxyInstance(classLoader, new Class[]{ISellHose.class}, handler1);
    proxy1.sellHose();

}
复制代码
租了一间房子。。。
买了一间房子。。。
复制代码

在main方法中咱们用到了Proxy这个类的方法,

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 复制代码

loder:类加载器,interfaces:代码要用来代理的接口, h:一个 InvocationHandler 对象 。

InvocationHandler 是一个接口,每一个代理的实例都有一个与之关联的 InvocationHandler 实现类,若是代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。

public interface InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
复制代码

InvocationHandler 内部只是一个 invoke() 方法,正是这个方法决定了怎么样处理代理传递过来的方法调用。

由于,Proxy 动态产生的代理会调用 InvocationHandler 实现类,因此 InvocationHandler 是实际执行者。

总结

  1. 静态代理,代理类须要本身编写代码写成。
  2. 动态代理,代理类经过 Proxy.newInstance() 方法生成。
  3. JDK实现的代理中无论是静态代理仍是动态代理,代理与被代理者都要实现两样接口,它们的实质是面向接口编程。CGLib能够不须要接口。
  4. 动态代理经过 Proxy 动态生成 proxy class,可是它也指定了一个 InvocationHandler 的实现类。

欢迎关注:

公众号微信
相关文章
相关标签/搜索