Java动态代理

一、代理的概念


  动态代理技术是整个java技术中最重要的一个技术,它是学习java框架的基础,不会动态代理技术,那么在学习Spring这些框架时是学不明白的。

  动态代理技术就是用来产生一个对象的代理对象的。在开发中为什么需要为一个对象产生代理对象呢?
  举一个现实生活中的例子:歌星或者明星都有一个自己的经纪人,这个经纪人就是他们的代理人,当我们需要找明星表演时,不能直接找到该明星,只能是找明星的代理人。比如刘德华在现实生活中非常有名,会唱歌,会跳舞,会拍戏,刘德华在没有出名之前,我们可以直接找他唱歌,跳舞,拍戏,刘德华出名之后,他干的第一件事就是找一个经纪人,这个经纪人就是刘德华的代理人(代理),当我们需要找刘德华表演时,不能直接找到刘德华了(刘德华说,你找我代理人商谈具体事宜吧!),只能是找刘德华的代理人,因此刘德华这个代理人存在的价值就是拦截我们对刘德华的直接访问!
  这个现实中的例子和我们在开发中是一样的,我们在开发中之所以要产生一个对象的代理对象,主要用于拦截对真实业务对象的访问。那么代理对象应该具有什么方法呢?代理对象应该具有和目标对象相同的方法

  所以在这里明确代理对象的两个概念:
    1、代理对象存在的价值主要用于拦截对真实业务对象的访问
    2、代理对象应该具有和目标对象(真实业务对象)相同的方法。刘德华(真实业务对象)会唱歌,会跳舞,会拍戏,我们现在不能直接找他唱歌,跳舞,拍戏了,只能找他的代理人(代理对象)唱歌,跳舞,拍戏,一个人要想成为刘德华的代理人,那么他必须具有和刘德华一样的行为(会唱歌,会跳舞,会拍戏),刘德华有什么方法,他(代理人)就要有什么方法,我们找刘德华的代理人唱歌,跳舞,拍戏,但是代理人不是真的懂得唱歌,跳舞,拍戏的,真正懂得唱歌,跳舞,拍戏的是刘德华,在现实中的例子就是我们要找刘德华唱歌,跳舞,拍戏,那么只能先找他的经纪人,交钱给他的经纪人,然后经纪人再让刘德华去唱歌,跳舞,拍戏。

二、java中的代理

2.1、"java.lang.reflect.Proxy"类介绍

  现在要生成某一个对象的代理对象,这个代理对象通常也要编写一个类来生成,所以首先要编写用于生成代理对象的类。在java中如何用程序去生成一个对象的代理对象呢,java在JDK1.5之后提供了一个"java.lang.reflect.Proxy"类,通过"Proxy"类提供的一个newProxyInstance方法用来创建一个对象的代理对象,如下所示:

1 static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

  newProxyInstance方法用来返回一个代理对象,这个方法总共有3个参数,ClassLoader loader用来指明生成代理对象使用哪个类装载器,Class<?>[] interfaces用来指明生成哪个对象的代理对象,通过接口指定,InvocationHandler h用来指明产生的这个代理对象要做什么事情。所以我们只需要调用newProxyInstance方法就可以得到某一个对象的代理对象了。

2.2、编写生成代理对象的类

  在java中规定,要想产生一个对象的代理对象,那么这个对象必须要有一个接口,所以我们第一步就是设计这个对象的接口,在接口中定义这个对象所具有的行为(方法)

  1、定义对象的行为接口

 1 package cn.gacl.proxy;
 2 
 3 /**
 4 * @ClassName: Person
 5 * @Description: 定义对象的行为
 6 * @author: 孤傲苍狼
 7 * @date: 2014-9-14 下午9:44:22
 8 *
 9 */ 
10 public interface Person {
11 
12     /**
13     * @Method: sing
14     * @Description: 唱歌
15     * @Anthor:孤傲苍狼
16     *
17     * @param name
18     * @return
19     */ 
20     String sing(String name);
21     /**
22     * @Method: sing
23     * @Description: 跳舞
24     * @Anthor:孤傲苍狼
25     *
26     * @param name
27     * @return
28     */ 
29     String dance(String name);
30 }

2、定义目标业务对象类

1 package cn.gacl.proxy;
 2 
 3 /**
 4 * @ClassName: LiuDeHua
 5 * @Description: 刘德华实现Person接口,那么刘德华会唱歌和跳舞了
 6 * @author: 孤傲苍狼
 7 * @date: 2014-9-14 下午9:22:24
 8 *
 9 */ 
10 public class LiuDeHua implements Person {
11 
12     public String sing(String name){
13         System.out.println("刘德华唱"+name+"歌!!");
14         return "歌唱完了,谢谢大家!";
15     }
16     
17     public String dance(String name){
18         System.out.println("刘德华跳"+name+"舞!!");
19         return "舞跳完了,多谢各位观众!";
20     }
21 }

3、创建生成代理对象的代理类

1 package cn.gacl.proxy;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 import java.lang.reflect.Proxy;
 6 
 7 /**
 8 * @ClassName: LiuDeHuaProxy
 9 * @Description: 这个代理类负责生成刘德华的代理人
10 * @author: 孤傲苍狼
11 * @date: 2014-9-14 下午9:50:02
12 *
13 */ 
14 public class LiuDeHuaProxy {
15 
16     //设计一个类变量记住代理类要代理的目标对象
17     private Person ldh = new LiuDeHua();
18     
19     /**
20     * 设计一个方法生成代理对象
21     * @Method: getProxy
22     * @Description: 这个方法返回刘德华的代理对象:Person person = LiuDeHuaProxy.getProxy();//得到一个代理对象
23     * @Anthor:孤傲苍狼
24     *
25     * @return 某个对象的代理对象
26     */ 
27     public Person getProxy() {
28         //使用Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)返回某个对象的代理对象
29         return (Person) Proxy.newProxyInstance(LiuDeHuaProxy.class
30                 .getClassLoader(), ldh.getClass().getInterfaces(),
31                 new InvocationHandler() {
32                     /**
33                      * InvocationHandler接口只定义了一个invoke方法,因此对于这样的接口,我们不用单独去定义一个类来实现该接口,
34                      * 而是直接使用一个匿名内部类来实现该接口,new InvocationHandler() {}就是针对InvocationHandler接口的匿名实现类
35                      */
36                     /**
37                      * 在invoke方法编码指定返回的代理对象干的工作
38                      * proxy : 把代理对象自己传递进来 
39                      * method:把代理对象当前调用的方法传递进来 
40                      * args:把方法参数传递进来
41                      * 
42                      * 当调用代理对象的person.sing("冰雨");或者 person.dance("江南style");方法时,
43                      * 实际上执行的都是invoke方法里面的代码,
44                      * 因此我们可以在invoke方法中使用method.getName()就可以知道当前调用的是代理对象的哪个方法
45                      */
46                     @Override
47                     public Object invoke(Object proxy, Method method,
48                             Object[] args) throws Throwable {
49                         //如果调用的是代理对象的sing方法
50                         if (method.getName().equals("sing")) {
51                             System.out.println("我是他的经纪人,要找他唱歌得先给十万块钱!!");
52                             //已经给钱了,经纪人自己不会唱歌,就只能找刘德华去唱歌!
53                             return method.invoke(ldh, args); //代理对象调用真实目标对象的sing方法去处理用户请求
54                         }
55                         //如果调用的是代理对象的dance方法
56                         if (method.getName().equals("dance")) {
57                             System.out.println("我是他的经纪人,要找他跳舞得先给二十万块钱!!");
58                             //已经给钱了,经纪人自己不会唱歌,就只能找刘德华去跳舞!
59                             return method.invoke(ldh, args);//代理对象调用真实目标对象的dance方法去处理用户请求
60                         }
61 
62                         return null;
63                     }
64                 });
65     }
66 }

测试代码:

1 package cn.gacl.proxy;
 2 
 3 public class ProxyTest {
 4     
 5     public static void main(String[] args) {
 6         
 7         LiuDeHuaProxy proxy = new LiuDeHuaProxy();
 8         //获得代理对象
 9         Person p = proxy.getProxy();
10         //调用代理对象的sing方法
11         String retValue = p.sing("冰雨");
12         System.out.println(retValue);
13         //调用代理对象的dance方法
14         String value = p.dance("江南style");
15         System.out.println(value);
16     }
17 }

运行结果如下:

  
  Proxy类负责创建代理对象时,如果指定了handler(处理器),那么不管用户调用代理对象的什么方法,该方法都是调用处理器的invoke方法。
  由于invoke方法被调用需要三个参数:代理对象、方法、方法的参数,因此不管代理对象哪个方法调用处理器的invoke方法,都必须把自己所在的对象、自己(调用invoke方法的方法)、方法的参数传递进来。

三、动态代理应用

  在动态代理技术里,由于不管用户调用代理对象的什么方法,都是调用开发人员编写的处理器的invoke方法(这相当于invoke方法拦截到了代理对象的方法调用)。并且,开发人员通过invoke方法的参数,还可以在拦截的同时,知道用户调用的是什么方法,因此利用这两个特性,就可以实现一些特殊需求,例如:拦截用户的访问请求,以检查用户是否有访问权限、动态为某个对象添加额外的功能。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章