首页 > 代码库 > 代理模式

代理模式

  java 的代理模式可以有静态代理模式和动态代理模式。下面的内容,大体的流程如下:

  

  所谓代理模式,就是代理处理其他类的操作。具体点就是,类似于北京的中介公司一样,自己没有实际的房屋和出租权,而是通过跟真正的房东商谈,代理出租房屋,从中谋取利益。这种模式的好处是,隐藏了真正的处理类,可以使代理类的操作简单化,而代理的操作可以复杂和有更多的操作。就如中介公司如果把房东透漏给你了,你就不会再经过中介去租房屋了,中介也不能再中介抽取钱了。

  (I)静态代理模式

  首先,看一下简单的静态代理模式。静态代理模式的类图如下:

其中,我定义了接口 PersonDao,实现类 PersonDaoImpl,代理类 PersonProxy,另外两个 After、Before 是我设置的后置方法类和前置方法的封装类。具体的代码如下:

(1)Dao 接口:

 1 /** 2  * 0.0.0.1 3  */ 4 package com.gaoqing.common.mode; 5  6 /** 7  * Person 到 接口 8  * @author gaoqing 9  * 2014-6-1710  */11 public interface PersonDao {12 13     public void info();14 15 }

上述 Dao 接口,只是定义了一个简单的 info() 方法,打印出用户信息的操作。

(2)DaoImpl 实现类:

 1 /** 2  * 0.0.0.1 3  */ 4 package com.gaoqing.common.mode; 5  6 /** 7  * Person dao 的实现类 8  * @author gaoqing 9  * 2014-6-1710  */11 public class PersonDaoImpl implements PersonDao {12 13     private String name;14     private String address;15     /**16      * 构造方法17      */18     public PersonDaoImpl() {19         20     }21 22     /**23      * 构造方法24      */25     public PersonDaoImpl(String name, String address) {26         super();27         this.name = name;28         this.address = address;29     }30 31     /**32      * @see com.gaoqing.common.mode.PersonDao#info()33      */34     @Override35     public void info() {36         37         System.out.println("姓名:" + name + "," + "地址:" + address);38 39     }40 41     /**42      * @return the name43      */44     public String getName() {45         return name;46     }47 48     /**49      * @param name the name to set50      */51     public void setName(String name) {52         this.name = name;53     }54 55     /**56      * @return the address57      */58     public String getAddress() {59         return address;60     }61 62     /**63      * @param address the address to set64      */65     public void setAddress(String address) {66         this.address = address;67     }68 }

(3)代理类如下:

 1 /** 2  * 0.0.0.1 3  */ 4 package com.gaoqing.common.mode; 5  6 /** 7  * 静态代理方法 8  * @author gaoqing 9  * 2014-6-2110  */11 public class StaticProxyTest implements PersonDao{12     13     /** PersonDao 方法 */14     private PersonDao personDao;15     /** 前置方法对象 */16     private Before before;17     /** 后置方法对象 */18     private After after;19 20     /**21      * 构造方法22      */23     public StaticProxyTest() {24         25     }26     27     /**28      * 构造方法29      * @param personDao PersonDao 对象30      * @param before 前置方法对象31      * @param after 后置方法对象32      */33     public StaticProxyTest(PersonDao personDao, Before before, After after) {34         super();35         this.personDao = personDao;36         this.before = before;37         this.after = after;38     }39 40     /**41     * @see com.gaoqing.common.mode.PersonDao#info()42     */43     @Override44     public void info() {45         //执行前置方法46         before.execute();47         48         //执行具体的请求方法49         personDao.info();50         51         //执行后置方法52         after.execute();53     }54 55     /**56      * @return the personDao57      */58     public PersonDao getPersonDao() {59         return personDao;60     }61 62     /**63      * @param personDao the personDao to set64      */65     public void setPersonDao(PersonDao personDao) {66         this.personDao = personDao;67     }68 69     /**70      * @return the before71      */72     public Before getBefore() {73         return before;74     }75 76     /**77      * @param before the before to set78      */79     public void setBefore(Before before) {80         this.before = before;81     }82 83     /**84      * @return the after85      */86     public After getAfter() {87         return after;88     }89 90     /**91      * @param after the after to set92      */93     public void setAfter(After after) {94         this.after = after;95     }96 }

上面的代理类的特点是:和 DaoImpl 的实现类一样,都实现了 PersonDao 接口,只是在自己的 info() 方法,调用实现类的具体方法,而在自己的操作中,添加了更多别的附加的操作。

  

  (II)动态代理模式

  如果只是仅仅使用简单的静态代理也可以实现某些面向切面的编程,比如插入日志什么的,可以将插入的内容放到 Before 和 After 类中。但是,如果我需要很多的代理类的时候,就需要像上面一样,写很多的类似的 PersonProxy 类,这样就很类了。随之产生的一种处理方式就出现了--动态代理。这样就可以只编写一个代理类,去代理所有自己想要代理类,更可以把那些相同的代码,分离到某些类中,集中进行管理,方便而且优雅。

  (II.1)JDK 实现

  我依旧使用上面的 PersonDao 和 PersonDaoImpl,具体的类图如下:

正如,在类图中的注释,这种动态代理的方式,是在 JDK 中,动态的生成一个类,该类和代理类一样,实现了相同的接口,拥有和代理类同样的方法。

(4)动态代理类如下:

 1 /** 2  * 0.0.0.1 3  */ 4 package com.gaoqing.common.mode; 5  6 import java.lang.reflect.Proxy; 7  8 /** 9  * 代理类10  * @author gaoqing11  * 2014-6-1712  */13 public class ProxyPerson {14 15     /** 被代理对象 */16     private Object obj;17     18     /** 调用处理类对象 */19     private MyInvocationHandler handler;20     21     /**22      * 构造方法23      */24     public ProxyPerson() {25         26     }27     28     /**29      * 构造方法30      * @param obj 被代理的对象31      * @param handler 具体的调用处理类32      */33     public ProxyPerson(Object obj, MyInvocationHandler handler) {34         super();35         this.obj = obj;36         this.handler = handler;37     }38 39     /**40      * 生成代理类41      * @author 高青42      * 2014-6-1743      * @return obj 代理类44      */45     public Object getProxyPerson(){46         47         return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);48     }49 50     /**51      * @return the obj52      */53     public Object getObj() {54         return obj;55     }56 57     /**58      * @param obj the obj to set59      */60     public void setObj(Object obj) {61         this.obj = obj;62     }63 64     /**65      * @return the handler66      */67     public MyInvocationHandler getHandler() {68         return handler;69     }70 71     /**72      * @param handler the handler to set73      */74     public void setHandler(MyInvocationHandler handler) {75         this.handler = handler;76     }77 }

(4)具体的调用处理类如下:

  1 /**  2  * 0.0.0.1  3  */  4 package com.gaoqing.common.mode;  5   6 import java.lang.reflect.InvocationHandler;  7 import java.lang.reflect.Method;  8   9 /** 10  * 自定义的调用处理器 11  * @author gaoqing 12  * 2014-6-17 13  */ 14 public class MyInvocationHandler implements InvocationHandler { 15      16     /** 被代理的类 */ 17     private Object obj; 18      19     /** 方法前对象 */ 20     private Before before; 21      22     /** 方法后对象 */ 23     private After after; 24  25     /** 26      * 构造方法 27      */ 28     public MyInvocationHandler() { 29          30     } 31  32     /** 33      * 构造方法 34      */ 35     public MyInvocationHandler(Object obj) { 36         super(); 37         this.obj = obj; 38     } 39  40     /** 41      * 构造方法 42      */ 43     public MyInvocationHandler(Object obj, Before before, After after) { 44         super(); 45         this.obj = obj; 46         this.before = before; 47         this.after = after; 48     } 49  50     /** 51      * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) 52      */ 53     @Override 54     public Object invoke(Object proxy, Method method, Object[] args) 55             throws Throwable { 56         /* 57          * 代理的具体处理方式 58          */ 59          60         //方法前操作 61         before.execute(); 62          63         Object invokeValue  =http://www.mamicode.com/ method.invoke(obj, args); 64          65         //方法后操作 66         after.execute(); 67          68         return invokeValue; 69     } 70  71     /** 72      * @return the obj 73      */ 74     public Object getObj() { 75         return obj; 76     } 77  78     /** 79      * @param obj the obj to set 80      */ 81     public void setObj(Object obj) { 82         this.obj = obj; 83     } 84  85     /** 86      * @return the before 87      */ 88     public Before getBefore() { 89         return before; 90     } 91  92     /** 93      * @param before the before to set 94      */ 95     public void setBefore(Before before) { 96         this.before = before; 97     } 98  99     /**100      * @return the after101      */102     public After getAfter() {103         return after;104     }105 106     /**107      * @param after the after to set108      */109     public void setAfter(After after) {110         this.after = after;111     }112 }

  (II.2)Cglib 实现

  从上面可以看到,使用 JDK 自带的接口实现动态代理的话,需要被代理的对象必须实现一个接口。这样,就存在了一种限制,如果我需要代理那些普通的类那该怎么办呢?这个时候可以使用 cglib 类库,它可以实现代理那些没有实现接口的类。Cglib 是一个优秀的动态代理框架,它的底层使用 ASM 在内存中动态的生成被代理类的一个子类。具体的 ASM 的使用,目前还没用过啊,现在只是简单的使用以下 cglib 。

  使用 cglib 框架实现动态代理时,需要导入两个包,一个是其依赖的 asm.jar,一个是 cglib.jar。注意:需要导入合适的包,如果包的版本不对,是会报错的,如果报错了,可以试着换下 cglib 的 jar 包。在这个代理的过程中,主要用到了 cglib 的:

(1)Enhancer – 主要的增强类,类似于 JDK 的 Proxy 类,产生具体的代理类

(2)MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户实现,就是将代理类请求的方法,进行拦截到被代理类对象进行处理,类似于 JDK 的 InvocationHandler 接口的 invoke()方法

(3)MethodProxy – JDKjava.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用

具体的实现如下:

 

 1 /** 2  * 0.0.0.1 3  */ 4 package com.gaoqing.common.mode; 5  6 import java.lang.reflect.Method; 7  8 import net.sf.cglib.proxy.Enhancer; 9 import net.sf.cglib.proxy.MethodInterceptor;10 import net.sf.cglib.proxy.MethodProxy;11 12 /**13  * 使用 Cglib 框架实现的代理类14  * @author gaoqing15  * 2014-6-1916  */17 public class DynamicProxyByCGLIBTest {18 19     /**20      * 构造方法21      */22     public DynamicProxyByCGLIBTest() {23         24     }25 26     /**27      * 主线程方法28      * @author 高青29      * 2014-6-1930      * @param args 参数字符串集31      * @return void 空32      */33     public static void main(String[] args) {34         35         //代理类36         final PersonDaoImpl personDaoImpl = new PersonDaoImpl("gaoqing", "beijing");37         38         //得到增强类39         Enhancer enhancer = new Enhancer();40         41         //设置增强类的父类42         enhancer.setSuperclass(PersonDaoImpl.class);43         44         enhancer.setCallback(new MethodInterceptor() {45             46             @Override47             public Object intercept(Object target, Method method, Object[] args,48                     MethodProxy proxy) throws Throwable {49                 50                 Object invoke = method.invoke(personDaoImpl, args);      //也可以使用 proxy 的 invokeSupre(target, args)51                 52                 return invoke;53             }54         });55         PersonDaoImpl personDaoImplProxy =  (PersonDaoImpl) enhancer.create();56         57         personDaoImplProxy.info();58 59     }60 }

 

  (III)代理模式的使用场景

  上述就是简单的代理模式的实现。动态代理的方式,可以实现面向 AOP 的编程,具体的使用是大家知道的动态的插入日志,添加事物,记录用户的操作记录等。具体的框架应用就是 Spring 的 AOP 实现,为它们提供为方法的Interception(拦截),在 hibernate 中,使用 Cglib 来代理单端single-ended(多对一和一对一)关联等等。