1.代理模式的定义:
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
2.组成的角色:
抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
3.代理模式的好处:
(1).职责清晰
真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。
(2).代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介解耦的作用和保护了目标对象的作用,并可以附加自己的操作。
(3).高扩展性
4.通过小例子理解代理模式:
public abstract class AbstractTarget { public abstract void dosomething(); }
public class ConcreteTarget extends AbstractTarget { @Override public void dosomething() { System.out.println("go home"); } }
public class MyProxy extends AbstractTarget { //静态代理 //编译期就已经明确指定了真实需要代理的类就是ConcreteTarget private AbstractTarget target = new ConcreteTarget(); @Override public void dosomething() { target.dosomething(); } }
public static void main(String[] args) { AbstractTarget target = new MyProxy(); target.dosomething(); }执行结果:
go home
这是一个最简单到代理模式的例子,客户端使用代理的dosomething()方法时,代理类委托给了真实角色ConcreteTarget执行,这就是代理模式。代理类成为客户端与真实角色直接的桥梁,在不同的场景下,这个桥梁可以起到很大的现实作用,这个在本文后面再讲述。
可以看到,该例中由于编译期已经知道代理的真实角色是谁了,所以这种代理模式又叫做静态代理。
//静态代理 //编译期就已经明确指定了真实需要代理的类就是ConcreteTarget private AbstractTarget target = new ConcreteTarget();
5.jdk动态代理
相对静态代理而言,动态代理是值编译器不知道代理的真实角色是谁,需要在运行期才能确定。
我们有一个超级明星的接口。
public interface SuperStar { void call(); void sing(); void dance(); }
有一个歌手实现了超级明星的接口。
public class Singer implements SuperStar { @Override public void call() { System.out.println("我正在休息,不要打扰我"); } @Override public void sing() { System.out.println("唱歌我在行,让我来"); } @Override public void dance() { System.out.println("跳舞找dancer去"); } }但是明星都很忙的,还要耍耍大牌,所以需要一个经纪人作为他的代理人,处理对外事宜。可是现在是用的jdk动态代理了,与先前的例子写法完全不一样了。jdk API中有个InvocationHandler调用处理器的接口。我们需要先定义一个自己的调用处理器。
public class BrokerInvocationHandler implements InvocationHandler { private SuperStar superStar; public BrokerInvocationHandler(SuperStar superStar){ this.superStar = superStar; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("sing")){ method.invoke(superStar,args); System.out.println("歌手同意了,我们准备准备就来"); return null; }else if (method.getName().equals("dance")){ System.out.println("跳舞别来找我们"); return null; }else if (method.getName().equals("call")){ System.out.println("人不在,有什么事找我好了"); return null; }else { return null; } } }
public class BrokerProxy { public static SuperStar newProxyInstance(SuperStar superStar){ return (SuperStar) Proxy.newProxyInstance(superStar.getClass().getClassLoader(), superStar.getClass().getInterfaces(),new BrokerInvocationHandler(superStar) ); } }有了这2个类,代理就算完成了。
public class Test { public static void main(String[] args) { SuperStar singer = BrokerProxy.newProxyInstance(new Singer()); singer.sing(); singer.call(); singer.dance();//true:说明singer的父类就是ProxySystem.out.println(singer.getClass().getSuperclass() == Proxy.class); }}
运行结果:
唱歌我在行,让我来
歌手同意了,我们准备准备就来
人不在,有什么事找我好了
跳舞别来找我们
true
jdk动态代理总结:
1.定义自己的InvocationHandler实现类BrokerInvocationHandler
2.通过Proxy类的newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)方法创建动态代理类
3.通过反射获取动态代理类的构造函数,并通过该构造函数创建代理类实例singer
4.被创建出来的实例singer继承了Proxy类(因此java动态代理只能代理接口,这是由java不支持多继承的设计决定的),实现了目标接口SuperStar
5.调用singer的任意方法,实际上都会先调用到BrokerInvocationHandler中的invoke方法,然后通过反射调用Method的invode方法调用实际被代理的方法
6.调用到BrokerInvocationHandler中的invoke方法时,可以获得被代理方法的信息,因此可以在真正调用invoke方法前、后写上自己的业务逻辑
5.代理模式的变形
在需要用比较通用和复杂的对象指针代替简单的指针的时候,使用 Proxy模式。下面是一些可以使用Proxy模式常见情况:
1) 远程代理(Remote Proxy)为一个位于不同的地址空间的对象提供一个本地的代理对象。这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又叫做大使(Ambassador)
2) 虚拟代理(Virtual Proxy)根据需要创建开销很大的对象。如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。
3) 保护代理(Protection Proxy)控制对原始对象的访问。保护代理用于对象应该有不同的访问权限的时候。
4) 智能指引(Smart Reference)取代了简单的指针,它在访问对象时执行一些附加操作。
5) Copy-on-Write代理:它是虚拟代理的一种,把复制(克隆)操作延迟到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象被用到的时候才被克隆。
6)缓存代理(Cache Proxy)
7)防火墙代理(Application Gateway)
8)同步代理