JDK的动态代理机制

来源:互联网 发布:淘淘商城项目完整源码 编辑:程序博客网 时间:2024/05/22 05:25

介绍       

代理模式 
        代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。

按照代理的创建时期,代理类可以分为两种:
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。 

        在JDK 1.3以后提供了动态代理的技术,允许开发者在运行期创建接口的代理实例。 JDK的动态代理主要涉及到java.lang.reflect包中的两个类:ProxyInvocationHandler。其中InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑,在并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编织在一起。

一)静态代理: 

1.Count.java

[java] view plaincopy
  1. <span style="font-size:16px;">package net.battier.dao;    
  2. /**   
  3.  * 定义一个账户接口   
  4.  *    
  5.  * @author Administrator   
  6.  *    
  7.  */     
  8. public interface Count {     
  9.     // 查看账户方法     
  10.     public void queryCount();     
  11.     // 修改账户方法     
  12.     public void updateCount();      
  13. }   </span>  
2.CountImpl.java

[java] view plaincopy
  1. package net.battier.dao.impl;     
  2. import net.battier.dao.Count;     
  3. /**   
  4.  * 委托类(包含业务逻辑)   
  5.  *    
  6.  * @author Administrator   
  7.  *    
  8.  */     
  9. public class CountImpl implements Count {  
  10.     @Override     
  11.     public void queryCount() {     
  12.         System.out.println("查看账户方法...");     
  13.     }     
  14.     @Override     
  15.     public void updateCount() {     
  16.         System.out.println("修改账户方法...");     
  17.     }     
  18. }     
3.CountProxy.java  

[java] view plaincopy
  1. package net.battier.dao.impl;     
  2.      
  3. import net.battier.dao.Count;     
  4.      
  5. /**   
  6.  * 这是一个代理类(增强CountImpl实现类)   
  7.  *    
  8.  * @author Administrator   
  9.  *    
  10.  */     
  11. public class CountProxy implements Count {     
  12.     private CountImpl countImpl;     
  13.      
  14.     /**   
  15.      * 覆盖默认构造器   
  16.      *    
  17.      * @param countImpl   
  18.      */     
  19.     public CountProxy(CountImpl countImpl) {     
  20.         this.countImpl = countImpl;     
  21.     }     
  22.      
  23.     @Override     
  24.     public void queryCount() {     
  25.         System.out.println("事务处理之前");     
  26.         // 调用委托类的方法;     
  27.         countImpl.queryCount();     
  28.         System.out.println("事务处理之后");     
  29.     }     
  30.      
  31.     @Override     
  32.     public void updateCount() {     
  33.         System.out.println("事务处理之前");     
  34.         // 调用委托类的方法;     
  35.         countImpl.updateCount();     
  36.         System.out.println("事务处理之后");     
  37.      
  38.     }     
  39.      
  40. }  

4.TestCount.java

[java] view plaincopy
  1. Java代码   
  2. package net.battier.test;     
  3. import net.battier.dao.impl.CountImpl;     
  4. import net.battier.dao.impl.CountProxy;     
  5. /**   
  6.  *测试Count类   
  7.  *    
  8.  * @author Administrator   
  9.  *    
  10.  */     
  11. public class TestCount {     
  12.     public static void main(String[] args) {     
  13.         CountImpl countImpl = new CountImpl();     
  14.         CountProxy countProxy = new CountProxy(countImpl);     
  15.         countProxy.updateCount();     
  16.         countProxy.queryCount();     
  17.     }     
  18. }    
       观察代码可以发现每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生过多的代理,而且,所有的代理操作除了调用的方法不一样之外,其他的操作都一样,则此时肯定是重复代码。解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能,那么此时就必须使用动态代理完成。 

二)动态代理

1.定义一个接口和实现 

[java] view plaincopy
  1. <span style="font-weight: normal;"><span style="font-size:16px;">package com.tech.service;  
  2. public interface PersonService {  
  3.     public String getPersonName(Integer personId);  
  4.     public void save(String name);  
  5.     public void update(Integer personId, String name);  
  6. }  
  7.   
  8. package com.tech.service.impl;  
  9. import com.tech.service.PersonService;  
  10. public class PersonServiceBean implements PersonService {     
  11.     public String user = null;  
  12.   
  13.     public PersonServiceBean(){};  
  14.     public PersonServiceBean(String user){  
  15.         this.user = user;  
  16.     }  
  17.       
  18.     @Override  
  19.     public String getPersonName(Integer personId) {  
  20.         // TODO Auto-generated method stub  
  21.         System.out.println("这是find方法");  
  22.         return this.user;  
  23.     }  
  24.   
  25.     @Override  
  26.     public void save(String name) {  
  27.         // TODO Auto-generated method stub  
  28.         System.out.println("这是save方法");  
  29.     }  
  30.   
  31.     @Override  
  32.     public void update(Integer personId, String name) {  
  33.         // TODO Auto-generated method stub  
  34.         System.out.println("这是update方法");  
  35.     }  
  36.     public String getUser() {  
  37.         return user;  
  38.     }  
  39.     public void setUser(String user) {  
  40.         this.user = user;  
  41.     }  
  42.   
  43. }</span></span>  
2.JDK动态代理代理类

[java] view plaincopy
  1. package com.tech.jdkproxy;  
  2.   
  3. import java.lang.reflect.InvocationHandler;  
  4. import java.lang.reflect.Method;  
  5. import java.lang.reflect.Proxy;  
  6.   
  7. import com.tech.service.impl.PersonServiceBean;  
  8.   
  9. /** 
  10.  *   
  11.  * 切面   
  12.  * @author ch 
  13.  * 
  14.  */  
  15. public class JDKProxyFactory implements InvocationHandler{  
  16.   
  17.     private Object proxyObject; //目标对象  
  18.   
  19.     /** 
  20.      * 绑定委托对象并返回一个代理类  
  21.      * @param proxyObject 
  22.      * @return 
  23.      */  
  24.     public Object createProxyInstance(Object proxyObject) {  
  25.         this.proxyObject = proxyObject;  
  26.           
  27.         //生成代理类的字节码加载器   
  28.         ClassLoader classLoader = proxyObject.getClass().getClassLoader();  
  29.         //需要代理的接口,被代理类实现的多个接口都必须在这里定义  (这是一个缺陷,cglib弥补了这一缺陷)    
  30.         Class<?>[] proxyInterface = proxyObject.getClass().getInterfaces();//new Class[]{};   
  31.           
  32.         //织入器,织入代码并生成代理类     
  33.         return Proxy.newProxyInstance(classLoader,  
  34.                 proxyInterface, this);  
  35.   
  36.     }  
  37.   
  38.     @Override  
  39.     public Object invoke(Object proxy, Method method, Object[] args)  
  40.             throws Throwable {  
  41.         PersonServiceBean bean = (PersonServiceBean)this.proxyObject;  
  42.         Object result = null;  
  43.         //控制哪些用户执行切入逻辑  
  44.         if(bean.getUser() != null) {  
  45.             //执行原有逻辑     
  46.             result = method.invoke(this.proxyObject, args);  
  47.         }  
  48.         return result;  
  49.     }  
  50. }  
3.测试类

[java] view plaincopy
  1. package com.tech.junit;  
  2.   
  3. import org.junit.BeforeClass;  
  4. import org.junit.Test;  
  5.   
  6. import com.tech.jdkproxy.JDKProxyFactory;  
  7. import com.tech.service.PersonService;  
  8. import com.tech.service.impl.PersonServiceBean;  
  9.   
  10. public class PersonTest {  
  11.   
  12.     @BeforeClass  
  13.     public static void setUpBeforeClass() throws Exception {  
  14.     }  
  15.   
  16.     @Test  
  17.     public void Test() {  
  18.         JDKProxyFactory factory = new JDKProxyFactory();  
  19.         PersonService bean = (PersonService) factory  
  20.                 .createProxyInstance(new PersonServiceBean("lucy"));  
  21.         //用户为lucy,有权限  
  22.         bean.save("abc");  
  23.           
  24.         PersonService bean2 = (PersonService) factory  
  25.             .createProxyInstance(new PersonServiceBean());  
  26.         //用户为null,没有权限,不输出  
  27.         bean2.save("abc");  
  28.     }  
  29. }  


但是,JDK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了

一、原理

      代理为控制要访问的目标对象提供了一种途径。当访问对象时,它引入了一个间接的层。JDK自从1.3版本开始,就引入了动态代理,并且经常被用来动态地创建代理。JDK的动态代理用起来非常简单,当它有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的继承的类,该怎么办?现在我们可以使用CGLIB包。

二、什么是cglib

     CGLIB是一个强大的高性能的代码生成包。

      1>它广泛的被许多AOP的框架使用,例如:Spring AOP和dynaop,为他们提供方法的interception(拦截);

       2>hibernate使用CGLIB来代理单端single-ended(多对一和一对一)关联(对集合的延迟抓取,是采用其他机制实现的);

       3>EasyMock和jMock是通过使用模仿(moke)对象来测试java代码的包。

     它们都通过使用CGLIB来为那些没有接口的类创建模仿(moke)对象。

三、底层

      CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM(Java字节码操控框架),来转换字节码并生成新的类。除了CGLIB包,脚本语言例如 Groovy和BeanShell,也是使用ASM来生成java的字节码。当不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。所以cglib包要依赖于asm包,需要一起导入。下图为cglib与一些框架和语言的关系(CGLIB Library and ASM Bytecode Framework)

      Spring AOP和Hibernate同时使用JDK的动态代理和CGLIB包。Spring AOP,如果不强制使用CGLIB包,默认情况是使用JDK的动态代理来代理接口。

四、实例场景模拟

1. 我们创建一个对Table操作的DAO类,提供了CRUD方法。 
    BookServiceBean.java

[java] view plaincopy
  1. package com.tech.cglibx;  
  2. public class BookServiceBean {  
  3.  public void create(){     
  4.         System.out.println("create() is running !");     
  5.     }     
  6.     public void query(){     
  7.         System.out.println("query() is running !");     
  8.     }     
  9.     public void update(){     
  10.         System.out.println("update() is running !");     
  11.     }     
  12.     public void delete(){     
  13.         System.out.println("delete() is running !");     
  14.     }     
  15. }  

    OK,它就是一个javaBean,提供了CRUD方法的javaBean。 
    下面我们创建一个DAO工厂,用来生成DAO实例。

[java] view plaincopy
  1. package com.tech.cglibx;  
  2. public class BookServiceFactory {  
  3.  private static BookServiceBean service = new BookServiceBean();  
  4.  private BookServiceFactory() {  
  5.  }  
  6.  public static BookServiceBean getInstance() {  
  7.   return service;  
  8.  }  
  9. }  

    接下来我们创建客户端,用来调用CRUD方法。

[java] view plaincopy
  1. public class Client {     
  2.     
  3.     public static void main(String[] args) {     
  4.         BookServiceBean service = BookServiceFactory.getInstance();   
  5.         doMethod(service);     
  6.     }     
  7.     public static void doMethod(BookServiceBean service){     
  8.         service.create();  
  9.         service.update();  
  10.         service.query();  
  11.         service.delete();   
  12.     }     
  13. }   

    OK,完成了,CRUD方法完全被调用了。

    当然这里并没有CGlib的任何内容。问题不会这么简单的就结束,新的需求来临了。

 
2. one day,Boss告诉我们这些方法不能开放给用户,只有“boss”才有权使用。怎么办,难道我们要在每个方法上面进行判断吗?好像这么做也太那啥了吧?对了,Proxy可能是最好的解决办法。jdk的代理就可以解决了。 好了我们来动手改造吧。等等jdk的代理需要实现接口,这样, 我们的dao类需要改变了。既然不想改动dao又要使用代理,我们这就请出CGlib。
我们只需新增一个权限验证的方法拦截器。

[java] view plaincopy
  1. package com.tech.cglibx;  
  2. import java.lang.reflect.Method;  
  3. import net.sf.cglib.proxy.Enhancer;  
  4. import net.sf.cglib.proxy.MethodInterceptor;  
  5. import net.sf.cglib.proxy.MethodProxy;  
  6. import org.apache.log4j.Logger;  
  7. public class MyCglibProxy implements MethodInterceptor{  
  8.  private Logger log=Logger.getLogger(MyCglibProxy.class);  
  9.  public Enhancer enhancer = new Enhancer();  
  10.  private String name;  
  11.    
  12.  public MyCglibProxy(String name) {  
  13.        this.name = name ;  
  14.  }  
  15.  /** 
  16.   * 根据class对象创建该对象的代理对象 
  17.   * 1、设置父类;2、设置回调 
  18.   * 本质:动态创建了一个class对象的子类 
  19.   *  
  20.   * @param cls 
  21.   * @return 
  22.   */  
  23.  public Object getDaoBean(Class cls) {  
  24.   enhancer.setSuperclass(cls);  
  25.   enhancer.setCallback(this);  
  26.   return enhancer.create();  
  27.  }  
  28.    
  29.  @Override  
  30.  public Object intercept(Object object, Method method, Object[] args,  
  31.    MethodProxy methodProxy) throws Throwable {  
  32.   log.info("调用的方法是:" + method.getName());  
  33.   //用户进行判断  
  34.   if(!"张三".equals(name)){   
  35.    System.out.println("你没有权限!");   
  36.    return null;   
  37.   }   
  38.   Object result = methodProxy.invokeSuper(object, args);  
  39.     
  40.   return result;  
  41.  }  
  42. }  

        当然不能忘了对我们的dao工厂进行修改,我们提供一个使用代理的实例生成方法。上面的类中已经提供了一个通用的获取代理实例的方法,没有特殊需求(如下3)的方式可以使用上面的方式获取代理对象。

[java] view plaincopy
  1. public static BookServiceBean getProxyInstance(MyCglibProxy myProxy){    
  2.      Enhancer en = new Enhancer();     
  3.      //进行代理     
  4.      en.setSuperclass(BookServiceBean.class);     
  5.      en.setCallback(myProxy);     
  6.      //生成代理实例     
  7.      return (BookServiceBean)en.create();     
  8.  } <span style="font-family: Arial, Verdana, sans-serif; white-space: normal; "> </span>  

     我们这就可以看看客户端的实现了。添加了两个方法用来验证不同用户的权限

[java] view plaincopy
  1. BookServiceBean service = BookServiceFactory.getProxyInstance(new MyCglibProxy("boss"));  
  2. service.create();  
  3. BookServiceBean service2 = BookServiceFactory.getProxyInstance(new MyCglibProxy("john"));  
  4. service2.create();  

OK,"boss"的正常执行,"john"的没有执行。 
看到了吗?简单的aop就这样实现了 
难道就这样结束了么? 

3.grd Boss又来训话了,不行不行,现在除了"boss"其他人都用不了了,现在不可以这样。必须使用开放查询功能。 
 哈哈,现在可难不倒我们了,因为我们使用了CGlib。当然最简单的方式是去修改我们的方法拦截器,不过这样会使逻辑变得复杂,且不利于维护。

还好CGlib给我们提供了方法过滤器(CallbackFilter),CallbackFilte可以明确表明,被代理的类中不同的方法,被哪个拦截器所拦截。

下面我们就来做个过滤器用来过滤query方法。 

[java] view plaincopy
  1. package com.tech.cglibx;  
  2. import java.lang.reflect.Method;  
  3. import net.sf.cglib.proxy.CallbackFilter;  
  4. public class MyProxyFilter implements CallbackFilter {  
  5.  @Override  
  6.  public int accept(Method arg0) {     
  7.         if(!"query".equalsIgnoreCase(arg0.getName()))     
  8.             return 0;     
  9.         return 1;     
  10.     }  
  11. }  

我们在工场中新增一个使用了过滤器的实例生成方法。 

[java] view plaincopy
  1. public static BookServiceBean getAuthInstanceByFilter(MyCglibProxy myProxy){    
  2.      Enhancer en = new Enhancer();     
  3.      en.setSuperclass(BookServiceBean.class);     
  4.      en.setCallbacks(new Callback[]{myProxy,NoOp.INSTANCE});     
  5.      en.setCallbackFilter(new MyProxyFilter());     
  6.      return (BookServiceBean)en.create();     
  7.  }  

   setCallbacks中定义了所使用的拦截器,其中NoOp.INSTANCE是CGlib所提供的实际是一个没有任何操作的拦截器, 
   他们是有序的,一定要和CallbackFilter里面的顺序一致。上面return返回(0/1)的就是返回的顺序。也就是说如果调用query方法就使用NoOp.INSTANCE进行拦截。
现在看一下客户端代码。

[java] view plaincopy
  1. BookServiceBean service = BookServiceFactory.getProxyInstanceByFilter(new MyCglibProxy("jhon"));  
  2.  service.create();  
  3.  BookServiceBean service2 = BookServiceFactory.getProxyInstanceByFilter(new MyCglibProxy("jhon"));  
  4.  service2.query();  


ok,现在"李四"也可以使用query方法了,其他方法仍然没有权限。 
当然这个代理的实现没有任何侵入性,无需强制让dao去实现接口。


***********************************************************************************************************************************************************************************

jdk的动态代理是基于接口的,必须实现了某一个或多个任意接口才可以被代理,并且只有这些接口中的方法会被代理。看了一下jdk带的动态代理api,发现没有例子实在是很容易走弯路,所以这里写一个加法器的简单示例。

?
1
2
3
4
5
6
7
// Adder.java
 
package test;
 
public interface Adder {
    int add(int a,int b);
}
?
1
2
3
4
5
6
7
8
9
10
// AdderImpl.java
 
package test;
 
public class AdderImplimplements Adder {
    @Override
    public int add(int a,int b) {
        return a + b;
    }
}
?
1
现在我们有一个接口Adder以及一个实现了这个接口的类AdderImpl,写一个Test测试一下。
?
1
2
3
4
5
6
7
8
9
10
11
// Test.java
 
package test;
 
public class Test {
    public static void main(String[] args) throws Exception {
        Adder calc = new AdderImpl();
        int result = calc.add(1,2);
        System.out.println("The result is " + result);
    }
}

很显然,控制台会输出:

?
1
The result is 3

然而现在我们需要在加法器使用之后记录一些信息以便测试,但AdderImpl的源代码不能更改,就像这样:

?
1
2
Proxy: invoke add() at 2009-12-16 17:18:06
The result is 3

动态代理可以很轻易地解决这个问题。我们只需要写一个自定义的调用处理器(实现接口java.lang.reflect.InvokationHandler),然后使用类java.lang.reflect.Proxy中的静态方法生成Adder的代理类,并把这个代理类当做原先的Adder使用就可以。

第一步:实现InvokationHandler,定义调用方法时应该执行的动作。

自定义一个类MyHandler实现接口java.lang.reflect.InvokationHandler,需要重写的方法只有一个:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// AdderHandler.java
 
package test;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
 
class AdderHandlerimplements InvocationHandler {
    /**
     * @param proxy 接下来Proxy要为你生成的代理类的实例,注意,并不是我们new出来的AdderImpl
     * @param method 调用的方法的Method实例。如果调用了add(),那么就是add()的Method实例
     * @param args 调用方法时传入的参数。如果调用了add(),那么就是传入add()的参数
     * @return 使用代理后将作为调用方法后的返回值。如果调用了add(),那么就是调用add()后的返回值
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        // ...
    }
}

使用代理后,这个方法将取代指定的所有接口中的所有方法的执行。在本例中,调用adder.add()方法时,实际执行的将是invoke()。所以为了有正确的结果,我们需要在invoke()方法中手动调用add()方法。再看看invoke()方法的参数,正好符合反射需要的所有条件,所以这时我们马上会想到这样做:

?
1
Object returnValue = method.invoke(proxy, args);

如果你真的这么做了,那么恭喜你,你掉入了jdk为你精心准备的圈套。proxy是jdk为你生成的代理类的实例,实际上就是使用代理之后adder引用所指向的对象。由于我们调用了adder.add(1, 2),才使得invoke()执行,如果在invoke()中使用method.invoke(proxy, args),那么又会使invoke()执行。没错,这是个死循环。然而,invoke()方法没有别的参数让我们使用了。最简单的解决方法就是,为MyHandler加入一个属性指向实际被代理的对象。所以,因为jdk的冷幽默,我们需要在自定义的Handler中加入以下这么一段:

?
1
2
3
4
5
6
// 被代理的对象
private Object target;
 
public AdderHandler(Object target) {
    this.target = target;
}

喜欢的话还可以加上getter/setter。接着,invoke()就可以这么用了:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// AdderHandler.java
 
package test;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Date;
 
class AdderHandlerimplements InvocationHandler {
    // 被代理的对象
    private Object target;
 
    public AdderHandler(Object target) {
        this.target = target;
    }
     
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        // 调用被代理对象的方法并得到返回值
        Object returnValue = method.invoke(target, args);
        // 调用方法前后都可以加入一些其他的逻辑
        System.out.println("Proxy: invoke " + method.getName() + "() at " +new Date().toLocaleString());
        // 可以返回任何想要返回的值
        return returnValue;
    }
}

第二步:使用jdk提供的java.lang.reflect.Proxy生成代理对象。

使用newProxyInstance()方法就可以生成一个代理对象。把这个方法的签名拿出来:

?
1
2
3
4
5
6
7
8
9
10
/**
 * @param loader 类加载器,用于加载生成的代理类。
 * @param interfaces 需要代理的接口。这些接口的所有方法都会被代理。
 * @param h 第一步中我们建立的Handler类的实例。
 * @return 代理对象,实现了所有要代理的接口。
 */
public static Object newProxyInstance(ClassLoader loader,
          Class<?>[] interfaces,
          InvocationHandler h)
throws IllegalArgumentException

这个方法会做这样一件事情,他将把你要代理的全部接口用一个由代码动态生成的类类实现,所有的接口中的方法都重写为调用InvocationHandler.invoke()方法。这个类的代码类似于这样:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 模拟Proxy生成的代理类,这个类是动态生成的,并没有对应的.java文件。
 
class AdderProxyextends Proxyimplements Adder {
    protected AdderProxy(InvocationHandler h) {
        super(h);
    }
 
    @Override
    public int add(int a,int b) {
        try {
            Method m = Adder.class.getMethod("add",new Class[] {int.class,int.class});
            Object[] args = {a, b};
            return (Integer) h.invoke(this, m, args);
        }catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }
}

据api说,所有生成的代理类都是Proxy的子类。当然,生成的这个类的代码你是看不到的,而且Proxy里面也是调用sun.XXX包的api生成;一般情况下应该是直接生成了字节码。然后,使用你提供的ClassLoader将这个类加载并实例化一个对象作为代理返回。

看明白这个方法后,我们来改造一下main()方法。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Test.java
 
package test;
 
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
 
public class Test {
    public static void main(String[] args) throws Exception {
        Adder calc = new AdderImpl();
         
        // 类加载器
        ClassLoader loader = Test.class.getClassLoader();
        // 需要代理的接口
        Class[] interfaces = {Adder.class};
        // 方法调用处理器,保存实际的AdderImpl的引用
        InvocationHandler h = new AdderHandler(calc);
        // 为calc加上代理
        calc = (Adder) Proxy.newProxyInstance(loader, interfaces, h);
         
        /* 什么?你说还有别的需求? */
        // 另一个处理器,保存前处理器的引用
        // InvocationHandler h2 = new XXOOHandler(h);
        // 再加代理
        // calc = (Adder) Proxy.newProxyInstance(loader, interfaces, h2);
         
        int result = calc.add(1,2);
        System.out.println("The result is " + result);
    }
}

输出结果会是什么呢?

?
1
2
Proxy: invoke add() at 2009-12-16 18:21:33
The result is 3