代理模式

  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-17
10  */
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-17
10  */
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     @Override
35     public void info() {
36         
37         System.out.println("姓名:" + name + "," + "地址:" + address);
38 
39     }
40 
41     /**
42      * @return the name
43      */
44     public String getName() {
45         return name;
46     }
47 
48     /**
49      * @param name the name to set
50      */
51     public void setName(String name) {
52         this.name = name;
53     }
54 
55     /**
56      * @return the address
57      */
58     public String getAddress() {
59         return address;
60     }
61 
62     /**
63      * @param address the address to set
64      */
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-21
10  */
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     @Override
44     public void info() {
45         //执行前置方法
46         before.execute();
47         
48         //执行具体的请求方法
49         personDao.info();
50         
51         //执行后置方法
52         after.execute();
53     }
54 
55     /**
56      * @return the personDao
57      */
58     public PersonDao getPersonDao() {
59         return personDao;
60     }
61 
62     /**
63      * @param personDao the personDao to set
64      */
65     public void setPersonDao(PersonDao personDao) {
66         this.personDao = personDao;
67     }
68 
69     /**
70      * @return the before
71      */
72     public Before getBefore() {
73         return before;
74     }
75 
76     /**
77      * @param before the before to set
78      */
79     public void setBefore(Before before) {
80         this.before = before;
81     }
82 
83     /**
84      * @return the after
85      */
86     public After getAfter() {
87         return after;
88     }
89 
90     /**
91      * @param after the after to set
92      */
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 gaoqing
11  * 2014-6-17
12  */
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-17
43      * @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 obj
52      */
53     public Object getObj() {
54         return obj;
55     }
56 
57     /**
58      * @param obj the obj to set
59      */
60     public void setObj(Object obj) {
61         this.obj = obj;
62     }
63 
64     /**
65      * @return the handler
66      */
67     public MyInvocationHandler getHandler() {
68         return handler;
69     }
70 
71     /**
72      * @param handler the handler to set
73      */
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  = 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 after
101      */
102     public After getAfter() {
103         return after;
104     }
105 
106     /**
107      * @param after the after to set
108      */
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 gaoqing
15  * 2014-6-19
16  */
17 public class DynamicProxyByCGLIBTest {
18 
19     /**
20      * 构造方法
21      */
22     public DynamicProxyByCGLIBTest() {
23         
24     }
25 
26     /**
27      * 主线程方法
28      * @author 高青
29      * 2014-6-19
30      * @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             @Override
47             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(多对一和一对一)关联等等。

 

posted @ 2014-06-22 00:07  绿 叶  阅读(1502)  评论(0编辑  收藏  举报