代理模式
顾名思义,代理模式提供了一个“代理人”对象,当“本人”太忙时,有些工作可以交给代理人。
好比如一个手机工厂,一开始它负责生产手机和卖手机,后来为了把人力充分利用到生产手机上,找了代理商来向客户卖手机。
几大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对象的功能时设置访问权限。