第十二讲:代理模式
我们去买书可以看做的是代理模式.书都是由出版社印刷出来的.但是我们买书不会去出版社买,而是通过出版社的代理:书店.我们看作是客户端,当当是代理对象,出版社是被代理对象.
这是代理模式的意义.
这是UML的结构图,一个类图.
Client是客户端.Subject是抽象主题角色.它是真实主题与代理主题的共同接口.比如说我们有个interface(接口)叫做卖书,比如主题是卖书,真实主题和代理主题它们都有一个核心,这个接口就是要卖书.出版社和当当它们共同要完成的主题就是要卖书.这是它们共同的.
RealSubject真实主题它也是要卖书,比如出版社,它也是要卖书,它不会直接卖给我们,它是给代理来提供卖书的.它是一个真实的对象.
Proxy代理主题角色,含有对真实主题角色的引用.当当可以给我们打折,打折就是在代理主题角色将客户端调用传递给真实主题对象之后执行某些操作,而不是单纯返回真实的对象.比如说在当当买书达到一定数量之后,它会送你代金券,这些都是出版社没有的,出版社只管卖书.而当当又打折又送代金券,所以我们会选择去当当买.
这就是一个代理的模式.
动态代理是什么?上面已知需要手动实现一个代理对象.动态代理的话我们无需手动创建一个代理对象以及做代理对象里面的操作.只要用JDK里面对代理的支持.
代理对象/代理实例的调用处理程序必须实现InvocationHandler这个接口,并且实现接口InvocaionHandler的invoke()方法.
第三步:生成一个代理实例.
Struts 2的拦截器的核心也是动态代理. 很多框架经常用到代理模式和JDK的动态代理.反射和JDK的动态代理经常被使用.JDK动态代理学习之前要先学习反射.
package com.ibeifeng.news; import java.lang.reflect.Proxy; public class MainClass { public static void main(String[] args) { RealSubject realSubject = new RealSubject(); MyHandler myHandler = new MyHandler();//代理实例的调用处理程序. myHandler.setRealSubject(realSubject); Subject proxySubject = (Subject) Proxy.newProxyInstance(RealSubject.class.getClassLoader(), realSubject.getClass().getInterfaces(), myHandler);//loader是代理类的类加载器, //不是特别明白的话可以参照反射这一章. proxySubject.sailBook();//代理对象没有一个类,那它是怎么来的呢?它是由Proxy这个代理类new出来的. //MyHandler与前面的ProxySubject完全不同,ProxySubject是实实在在的一个类,MyHandler是用来做处理的. //ProxySubject有一个sailBook()方法,而MyHandler根本就没有sailBook()方法. //所以不要把MyHandler实例化了,MyHandler是无法实例化的. } }
package com.ibeifeng.news; //代理主题角色的调用处理程序,实现了InvocationHandler接口 import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MyHandler implements InvocationHandler { private RealSubject realSubject; //持有真实主题角色的引用.因为还是要卖书的. /* public RealSubject getRealSubject() { return realSubject; }*/ public void setRealSubject(RealSubject realSubject) { this.realSubject = realSubject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //这里没有了代理主题角色ProxySubject,代理对象现在是由JDK自动生成 Object result; dazhe(); result = method.invoke(realSubject, args);//代理对象的调用处理程序拦截真实主题角色的卖书方法. //给你强制执行dazhe() //method.invoke(realSubject, args);调用realSubject的方法,到底是哪个方法呢? //客户端获取这个代理对象的时候调用哪个方法这里就会执行哪个方法. //客户端获取proxySubject的时候调用sailBook()方法这里就会执行realSubject的sailBook()方法. //调用处理程序拦截的就是realSubject的sailBook()方法,把参数args也给进去. give(); return result; } public void dazhe(){ System.out.println("打折"); } public void give(){ System.out.println("赠送代金券"); } }
package com.ibeifeng.news; //真实主题角色:出版社 public class RealSubject implements Subject{ @Override public void sailBook() { System.out.println("卖书"); } }
package com.ibeifeng.news; //抽象主题角色:卖书 public interface Subject { //private void sailBook(); public void sailBook(); }
以上是JDK动态代理的代码.
下面的是代理模式的代码.
//客户端使用的时候只需要使用到代理主题角色. //这样客户端就可以不要真实主题角色了,直接调用代理主题角色,在代理主题角色里面去处理一些操作. public class MainClass { public static void main(String[] args) { //Subject realSubject = new RealSubject(); //RealSubject realSubject = new RealSubject(); //realSubject.sailBook();//从出版社卖书 //Subject proxySubject = new ProxySubject(); ProxySubject proxySubject = new ProxySubject(); //proxySubject.setRealSubject(realSubject);//set方法有无必要呢?可以不要这个set方法. proxySubject.sailBook();//从当当卖书 //你里面怎么样我不管,我只管在这里买书,当当你就要卖给我. } }
//代理主题角色:当当含有对真实主题角色(RealSubject):出版社的引用. public class ProxySubject implements Subject{//代理主题角色也是实现抽象主题角色的,因为当当也是要卖书的. private RealSubject realSubject; /* public RealSubject getRealSubject() { return realSubject; } public void setRealSubject(RealSubject realSubject) { this.realSubject = realSubject; }*/ @Override public void sailBook() { //不是简简单单卖书了,还有打折和代金券.其实就是卖书之前和之后执行两个不同的方法. dazhe();//调用它之前可以打折 //卖书的话书从哪里来?书是出版社的,所以我们还是要调用它.这就是代理主题角色持有真实主题角色的引用的原因. if(realSubject==null){ realSubject = new RealSubject(); } realSubject.sailBook();//调用真实主题角色卖书 give(); } public void dazhe(){ System.out.println("打折"); } public void give(){ System.out.println("赠送代金券"); } }
//真实主题角色:出版社 public class RealSubject implements Subject{ @Override public void sailBook() { System.out.println("卖书"); } }
//抽象主题角色:卖书 public interface Subject { //private void sailBook(); public void sailBook(); }