首页 > 代码库 > 动态代理详解

动态代理详解

1 学习动态代理的目的
  动态代理技术都是在框架中使用,例如:Struts1、Struts2、Spring和Hibernate中都使用了动态代理技术。如果你不想自己写个框架,那么你基本上是用上不动态代理技术的。学习动态代理技术的目的是为了更好的理解框架内部的原理,也就是说是为了将来学习框架打基础!动态代理技术有点难度!而且明白了动态代理技术可能一时也想不到他适合在什么情况下使用它。这些东西都会在学习框架时渐渐明白。

2 运行时实现指定的接口
  想实现某个接口,你需要写一个类,然后在类名字的后面给出“implements”XXX接口。这才是实现某个接口:

public interface MyInterface {
  void fun1();
  void fun2();
}
public class MyInterfaceImpl implements MyInterface {
  public void fun1() {
    System.out.println("fun1()");
  }
  public void fun2() {
    System.out.println("fun2()");
  }
}

  上面的代码对我们来说没有什么新鲜感,我们要说的是动态代理技术可以通过一个方法调用就可以生成一个对指定接口的实现类对象。

Class[] cs = {MyInterface.class};
MyInterface mi = (MyInterface)Proxy.newProxyInstance(loader, cs, h);

  上面代码中,Proxy类的静态方法newProxyInstance()方法生成了一个对象,这个对象实现了cs数组中指定的接口。没错,返回值mi是MyInterface接口的实现类。你不要问这个类是哪个类,你只需要知道mi是MyInterface接口的实现类就可以了。你现在也不用去管loader和h这两个参数是什么东东,你只需要知道,Proxy类的静态方法newProxyInstance()方法返回的方法是实现了指定接口的实现类对象,甚至你都没有看见实现类的代码。
  动态代理就是在运行时生成一个类,这个类会实现你指定的一组接口,而这个类没有.java文件,是在运行时生成的,你也不用去关心它是什么类型的,你只需要知道它实现了哪些接口即可。

3 newProxyInstance()方法的参数
  Proxy类的newInstance()方法有三个参数:
    ClassLoader loader:它是类加载器类型,你不用去理睬它,你只需要知道怎么可以获得它就可以了:MyInterface.class.getClassLoader()就可以获取到ClassLoader对象,没错,只要你有一个Class对象就可以获取到ClassLoader对象;
    Class[] interfaces:指定newProxyInstance()方法返回的对象要实现哪些接口,没错,可以指定多个接口,例如上面例子只我们只指定了一个接口:Class[] cs = {MyInterface.class};
    InvocationHandler h:它是最重要的一个参数!它是一个接口!它的名字叫调用处理器!你想一想,上面例子中mi对象是MyInterface接口的实现类对象,那么它一定是可以调用fun1()和fun2()方法了,难道你不想调用一下fun1()和fun2()方法么,它会执行些什么东东呢?其实无论你调用代理对象的什么方法,它都是在调用InvocationHandler的invoke()方法!

public static void main(String[] args) {
  Class[] cs = {MyInterface.class};
  ClassLoader loader = MyInterface.class.getClassLoader();
  InvocationHandler h = new InvocationHandler() {
    public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
      System.out.println("无论你调用代理对象的什么方法,其实都是在调用invoke()...");
      return null;
    }
  };
  MyInterface mi = (MyInterface)Proxy.newProxyInstance(loader, cs, h);
  mi.fun1();
  mi.fun2();
}

  InvocationHandler接口只有一个方法,即invoke()方法!它是对代理对象所有方法的唯一实现。也就是说,无论你调用代理对象上的哪个方法,其实都是在调用InvocationHandler的invoke()方法。

  想象中的类:

class X implements MyInterface {
  private InvocationHandler h;
  public X(InvocationHandler h) {
    this.h = h;
  }
  public void fun1() {
    h.invoke();
  }
  public void fun2() {
    h.invoke();
  }
}

  注意,X类是我们用来理解代理对象与InvocationHandler之间的关系的,但它是不存在的类。是我们想象出来的!也就是说,它是用来说明,无论你调用代理对象的哪个方法,最终调用的都是调用处理器的invoke()方法。

4 InvocationHandler的invoke()方法
  InvocationHandler的invoke()方法的参数有三个:
    Object proxy:代理对象,也就是Proxy.newProxyInstance()方法返回的对象,通常我们用不上它;
    Method method:表示当前被调用方法的反射对象,例如mi.fun1(),那么method就是fun1()方法的反射对象;
    Object[] args:表示当前被调用方法的参数,当然mi.fun1()这个调用是没有参数的,所以args是一个零长数组。
  最后要说的是invoke()方法的返回值为Object类型,它表示当前被调用的方法的返回值,当然mi.fun1()方法是没有返回值的,所以invoke()返回的就必须是null了。

public static void main(String[] args) {
  Class[] cs = {MyInterface.class};
  ClassLoader loader = MyInterface.class.getClassLoader();
  InvocationHandler h = new InvocationHandler() {
    public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
      System.out.println("当前调用的方法是:" + method.getName());
      return null;
    }
  };
  MyInterface mi = (MyInterface)Proxy.newProxyInstance(loader, cs, h);
  mi.fun1();
  mi.fun2();
}
//当前调用的方法是:fun1
//当前调用的方法是:fun2

5 动态代理的用途
  动态代理的用途与装饰模式很相似,就是为了对某个对象进行增强。所有使用装饰者模式的案例都可以使用动态代理来替换。
  下面我们用一个例子来说明动态代理的用途!
  我们来写一个Waiter接口,它只有一个serve()方法。MyWaiter是Waiter接口的实现类:

public interface Waiter {
  public void serve();
}
public class MyWaiter implements Waiter {
  public void serve() {
    System.out.println("服务...");
  }
}

  现在我们要对MyWaiter对象进行增强,要让它在服务之前以及服务之后添加礼貌用语,即在服务之前说“您好!”,在服务之后说:“很高兴为您服务!”。

public class MainApp1 {
  public static void main(String[] args) {
    ClassLoader loader = MainApp1.class.getClassLoader();
    Class[] cs = {Waiter.class};
    Waiter target = new MyWaiter();
    MyInvocationHandler h = new MyInvocationHandler(target);
    Waiter waiter = (Waiter)Proxy.newProxyInstance(loader, cs, h);
    waiter.serve();
  }
}
class MyInvocationHandler implements InvocationHandler {
  public Waiter target;
  public MyInvocationHandler(Waiter target) {
    this.target = target;
  }
  public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
    System.out.println("您好!");
    Object result = method.invoke(target, args);
    System.out.println("很高兴为您服务!");
    return result;
  }
}

 实例(AOP):

技术分享
 1 import org.junit.Test;
 2 /*
 3  * 目标是让目标对象和增强都可以切换!
 4  */
 5 public class Demo3 {
 6     @Test
 7     public void fun1() {
 8         ProxyFactory factory = new ProxyFactory();//创建工厂
 9         factory.setTargetObject(new ManWaiter());//设置目标对象
10         factory.setBeforeAdvice(new BeforeAdvice() {//设置前置增强
11             public void before() {
12                 System.out.println("您好不好!");
13             }
14         });
15         
16         factory.setAfterAdvice(new AfterAdvice() {//设置后置增强
17             public void after() {
18                 System.out.println("再见不见!");
19             }
20         });
21         Waiter waiter = (Waiter)factory.createProxy();
22         waiter.shouQian();
23     }
24 }
Demo3
技术分享
 1 import java.lang.reflect.InvocationHandler;
 2 import java.lang.reflect.Method;
 3 import java.lang.reflect.Proxy;
 4 
 5 /**
 6  * 它用来生成代理对象
 7  * 它需要所有的参数
 8  * * 目标对象
 9  * * 增强
10  * @author cxf
11  */
12 /**
13  * 1. 创建代理工厂
14  * 2. 给工厂设置三样东西:
15  *   * 目标对象:setTargetObject(xxx);
16  *   * 前置增强:setBeforeAdvice(该接口的实现)
17  *   * 后置增强:setAfterAdvice(该接口的实现)
18  * 3. 调用createProxy()得到代理对象
19  *   * 执行代理对象方法时:
20  *   > 执行BeforeAdvice的before()
21  *   > 目标对象的目标方法
22  *   > 执行AfterAdvice的after()
23  * @author cxf
24  *
25  */
26 public class ProxyFactory {
27     private Object targetObject;//目标对象
28     private BeforeAdvice beforeAdvice;//前置增强
29     private AfterAdvice afterAdvice;//后置增强
30     /**
31      * 用来生成代理对象
32      * @return
33      */
34     public Object createProxy() {
35         /*
36          * 1. 给出三大参数
37          */
38         ClassLoader loader = this.getClass().getClassLoader();
39         Class[] interfaces = targetObject.getClass().getInterfaces();
40         InvocationHandler h = new InvocationHandler() {
41             public Object invoke(Object proxy, Method method, Object[] args)
42                     throws Throwable {
43                 /*
44                  * 在调用代理对象的方法时会执行这里的内容
45                  */
46                 // 执行前置增强
47                 if(beforeAdvice != null) {
48                     beforeAdvice.before();
49                 }
50                 
51                 Object result = method.invoke(targetObject, args);//执行目标对象的目标方法
52                 // 执行后置增强
53                 if(afterAdvice != null) {
54                     afterAdvice.after();
55                 }
56                 
57                 // 返回目标对象的返回值
58                 return result;
59             }
60         };
61         /*
62          * 2. 得到代理对象
63          */
64         Object proxyObject = Proxy.newProxyInstance(loader, interfaces, h);
65         return proxyObject;
66     }
67     
68     
69     public Object getTargetObject() {
70         return targetObject;
71     }
72     public void setTargetObject(Object targetObject) {
73         this.targetObject = targetObject;
74     }
75     public BeforeAdvice getBeforeAdvice() {
76         return beforeAdvice;
77     }
78     public void setBeforeAdvice(BeforeAdvice beforeAdvice) {
79         this.beforeAdvice = beforeAdvice;
80     }
81     public AfterAdvice getAfterAdvice() {
82         return afterAdvice;
83     }
84     public void setAfterAdvice(AfterAdvice afterAdvice) {
85         this.afterAdvice = afterAdvice;
86     }
87 }
ProxyFactory
技术分享
1 public class ManWaiter implements Waiter {
2     public void serve() {
3         System.out.println("服务中...");
4     }
5     
6     public void shouQian() {
7         System.out.println("混蛋,给我钱!");
8     }
9 }
ManWaiter
技术分享
1 // 服务员
2 public interface Waiter {
3     // 服务
4     public void serve();
5     public void shouQian();
6 }
Waiter
技术分享
1 public interface AfterAdvice {
2     public void after();
3 }
AfterAdvice
技术分享
1 /**
2  * 前置增强
3  * @author cxf
4  *
5  */
6 public interface BeforeAdvice {
7     public void before();
8 }
BeforeAdvice

动态代理详解