定义:
为其他对象提供一种代理以控制对这个对象的访问。
Proxy代理模式是一种结构型设计模式,主要解决的问题是:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层,这个访问层就是这个对象的代理。
一、简单代理模式
其简单的通用类图如下:
代理模式涉及的角色:
1. 抽象主题角色(Subject):声明了代理主题和真实主题的公共接口,使任何需要真实主题的地方都能用代理主题代替.
2. 代理主题角色(Proxy):含有真实主题的引用,从而可以在任何时候操作真实主题,代理主题功过提供和真实主题相同的接口,使它可以随时代替真实主题.代理主题通过持有真实主题的引用,不但可以控制真实主题的创建或删除,可以在真实主题被调用前进行拦截,或在调用后进行某些操作.
3. 真实代理对象(RealSubject):定义了代理角色所代表的具体对象.
其源代码如下:
public interface Subject { public void request(); }
public class RealSubject implements Subject { private String name; //默认的真实角色 public RealSubject(){ this.name="piao"; } //由外部指定真实角色 public RealSubject(String name){ this.name=name; } @Override public void request() { System.out.println(this.name+" request..."); } }
public class Proxy implements Subject { private Subject subject=null; //默认的被代理者 public Proxy(){ this.subject=new RealSubject(); } //创建一个指定名称的被代理对象 public Proxy(String name){ this.subject=new RealSubject(name); } //从外部通过构造函数传递被代理对象 public Proxy(Subject subject){ this.subject=subject; } @Override public void request() { this.before(); this.subject.request(); this.after(); } //预处理 private void before(){ System.out.println("before......"); } //善后处理 private void after(){ System.out.println("after......"); } }
二、强制代理
所谓强制代理就是说要使用真实角色中的业务逻辑,只能使用真实角色指定的代理,其他的代理,或者是真实角色本身都不能访问这部分业务逻辑。
Proxy和RealSubject两个类互相耦合,不太好理解,其实可以把他们想象成一对一的关系。其实现源码如下:
public interface Subject { public void request(); public Subject getProxy(); }
/** *强制代理,即若要使用真实角色中的业务逻辑,只能使用真实角色指定的代理, *其他的代理,或者是真实角色本身都不能访问这部分业务逻辑。 */ public class RealSubject implements Subject { private String name; private Proxy proxy; //默认的真实角色 public RealSubject(){ this.name="piao"; } //由外部指定真实角色 public RealSubject(String name){ this.name=name; } @Override public void request() { if(this.isProxy()) System.out.println(this.name+" request..."); else System.out.println("请使用指定的代理访问"); } //获得属于本真实主角的代理。 //注意,这里new一个代理的时候,一定要用new Proxy(this),把本真实角色的实例,传递给代理类,这样两者才互相绑定好了。 //不能用其他的构造方法,否则新建的代理类会再创建另一个真实角色,这个时候,这个代理类中维护的 //就不是本真实角色了,而是另外一个真实角色。 @Override public Subject getProxy() { this.proxy=new Proxy(this); return this.proxy; } //检验是否是代理访问 private boolean isProxy(){ if(this.proxy==null){ return false; } return true; } }
public class Proxy implements Subject { private Subject subject=null; //从外部通过构造函数传递被代理对象 public Proxy(Subject subject){ this.subject=subject; } @Override public void request() { this.before(); this.subject.request(); this.after(); } @Override public Subject getProxy() { return this; } //预处理 private void before(){ System.out.println("before......"); } //善后处理 private void after(){ System.out.println("after......"); } }
public class Client { public static void main(String[] args) { //直接使用主题角色,不能正常进行业务处理 Subject role1=new RealSubject("suo"); role1.request(); //在外部指定一个代理类,这个代理类,并不是主题对象所指定的代理类 //在这里proxy2理论上说也是role2的一个代理,但是这个代理并没有经过role2的认可, //role2只认可它指定的代理类(即在RealSubject类中的成员变量proxy),只有它指定的代理类,才能访问它的业务逻辑。 Subject role2=new RealSubject("suo"); Proxy proxy2=new Proxy(role2); proxy2.request(); //使用role3指定的代理,就可以正常访问role3的业务逻辑了 Subject role3=new RealSubject("piao"); Subject proxy3=role3.getProxy(); proxy3.request(); } }
三、动态代理
动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定代理哪一个对象。面向切面编程(AOP),其核心就是实现了动态代理机制。其简单类图如下:
其中InvocationHandler是JDK提供的动态代理的接口,对被代理的方法进行代理,其实现类的作用是用来对被代理的类所实现的接口中的方法进行拦截处理的。两个系别类的关联此处是靠场景类关联起来的,在场景类中,调用了如下的方法,根据主题对象,生成了相应的代理类,实现代码如下:
public interface Subject { //业务逻辑 public void request(); public void response(); }
public class RealSubject implements Subject { private String name; //默认的真实角色 public RealSubject(){ this.name="piao"; } //由外部指定真实角色 public RealSubject(String name){ this.name=name; } @Override public void request() { System.out.println(this.name+" request..."); } @Override public void response() { System.out.println(this.name+" response..."); } }
public class ConcreteHandler implements InvocationHandler { //被代理的实例 Object obj=null; public ConcreteHandler(Object obj){ this.obj=obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result=method.invoke(this.obj, args); if(method.getName().equalsIgnoreCase("request")){ System.out.println("piao is requesting..."); } return result; } }
public class Client { public static void main(String[] args) { Subject role=new RealSubject(); //定义被代理类实现的接口中的方法的操作,这即是AOP编程,在这个类中对方法进行拦截过滤 InvocationHandler handler=new ConcreteHandler(role); //被代理类的类加载器 ClassLoader cl=role.getClass().getClassLoader(); //用定义的InvocationHandler和被代理类所实现的接口,以及类加载器实例化一个代理类对象 Subject proxy=(Subject)Proxy.newProxyInstance(cl, new Class[]{Subject.class}, handler); proxy.request(); proxy.response(); } }
注意Proxy.newProxyInstance(cl, new Class[]{Subject.class}, handler);这个方法的调用,参数中第一个参数表示的是主题对象的类加载器,第二个参数是主题对象实现的所有接口,第三个参数是对主题对象实现的接口中的方法的操作。不用看源码也知道这个方法用了反射,并且根据传递进去的接口,和对接口中方法的操作,可以构造出和我们上面写的类似的代理类。
代理模式的优点:
1、职责清晰
真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务。
2、高可扩展性
具体主题对象(RealSubject)随时都会发生变化,只要它实现了接口,代理类不用做任何修改就可以拥抱这种变化。