代理模式

顾名思义,代理模式提供了一个“代理人”对象,当“本人”太忙时,有些工作可以交给代理人。
好比如一个手机工厂,一开始它负责生产手机和卖手机,后来为了把人力充分利用到生产手机上,找了代理商来向客户卖手机。

几大roles

  • Subject (目标接口/抽象类)
  • RealSubject (“本人”)
  • Proxy (“代理人”)
  • Client (用户或主程序)

UML图看清关系:
图片说明

好处

  • 能够在目标对象实现的基础上,扩展功能;
  • 能实现业务的分工,可以将公共的操作交给代理人,减少代码冗余性。
  • 在真正需要“本人”才能做的“重活时”再生成“本人”实例,使用代理人来提升速度。

有主要三大形式

  • 静态代理
  • 动态代理 (JDK代理)
  • Cglib代理 (属于动态代理)

静态代理

可以按照上面的UML图写出一个例子程序,这里就不展示了。
该种方式的缺点是:
一个真实对象就会产生一个代理对象,代码量会翻倍,开发效率会变低。

JDK代理

本质是反射机制
JDK实现代理只需要使用newProxyInstance方法。
我们可以通过一开始举的手机商家的例子,代码实现:

//目标接口
public interface SellPhone {
    public void sell();
    public void produce();
}

//本人实现了两个功能
public class Real implements SellPhone{
    @Override
    public void sell() {
        System.out.println("直接卖手机");
    }

    @Override
    public void produce() {
        System.out.println("生产手机");
    }
}

//生产代理类的对象,就像是代理类工厂
public class ProxyInvocationHandler implements InvocationHandler {

    private SellPhone sellPhone;
    public void setSellPhone(SellPhone sellPhone){
        this.sellPhone = sellPhone;
    }

    //通过该方法反射得到代理对象
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),sellPhone.getClass().getInterfaces(),this);
    }

    //代理类来卖手机
    public void sell(){
        System.out.println("卖手机");
    }

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

        Object result = method.invoke(sellPhone, args);
        //加入卖手机方法
        sell();
        return result;
    }
}

public class Client {
    public static void main(String[] args) {
        SellPhone real = new Real();
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        pih.setSellPhone(real);
        SellPhone proxy = (SellPhone) pih.getProxy();
        proxy.produce();
    }
}

结果:
图片说明
从结果可以看出:
我们卖手机调用的是代理类的方法,而不是本人的方法。
JDK这个ProxyInvocationHandler 也可以实现其他类的代理,只要把加载器,接口和当前对象传入Proxy.newProxyInstanc方法即可。

Cglib代理

静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理-这就是Cglib代理。
Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP,实现方法拦截。

使用Cglib代理,需要导入cglib.jar


代理模式的变体

  • HTTP代理
    它是位于Web服务器和HTTP客户端之间,为Web页面提供高速缓存等功能的软件,我们可以认为它是一种代理模式。
  • 远程代理
    可以让我们不用在意“本人”是否在远程网络上,相当于在本地建立了一个代理对象。
  • Access代理
    在调用RealSubject对象的功能时设置访问权限。