Java动态代理探讨

代理模式:html

  代理模式是经常使用的java设计模式,他的特征是代理类与委托类有一样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及过后处理消息等。经过代理模式,能够延迟建立对象,限制访问某个对象,也就是说,提供一组方法给普通用户,特别方法给管理员用户。java

UML图:git

简单结构示意图:程序员

  为了保持行为的一致性,代理类和委托类一般会实现相同的接口,因此在访问者看来二者没有丝毫的区别。github

按照代理的建立时期,代理类能够分为两种:设计模式

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

静态代理:dom

  为了帮助理解代理模式,来看一下静态代理的示例代码(代码摘自里):ide

Count.java 函数

 1 /** 
 2  * 定义一个帐户接口 
 3  * @author Administrator 
 4  */  
 5 public interface Count {  
 6     // 查看帐户方法  
 7     public void queryCount();  
 8     // 修改帐户方法  
 9     public void updateCount();  
10 }  

CountImpl.java 工具

 1 /** 
 2  * 委托类(包含业务逻辑) 
 3  * @author Administrator 
 4  */  
 5 public class CountImpl implements Count {  
 6   
 7     @Override  
 8     public void queryCount() {  
 9         System.out.println("查看帐户方法...");  
10     }  
11   
12     @Override  
13     public void updateCount() {  
14         System.out.println("修改帐户方法...");  
15     }  
16 }

CountProxy.java

 1 public class CountProxy implements Count {  
 2     private CountImpl countImpl;  
 3     /** 
 4      * 覆盖默认构造器 
 5      * @param countImpl 
 6      */  
 7     public CountProxy(CountImpl countImpl) {  
 8         this.countImpl = countImpl;  
 9     }  
10   
11     @Override  
12     public void queryCount() {  
13         System.out.println("事务处理以前");  
14         // 调用委托类的方法;  
15         countImpl.queryCount();  
16         System.out.println("事务处理以后");  
17     }  
18   
19     @Override  
20     public void updateCount() {  
21         System.out.println("事务处理以前");  
22         // 调用委托类的方法;  
23         countImpl.updateCount();  
24         System.out.println("事务处理以后");  
25     }  
26 }  

TestCount.java 

 1 /** 
 2  *测试Count类 
 3  * @author Administrator 
 4  */  
 5 public class TestCount {  
 6     public static void main(String[] args) {  
 7         CountImpl countImpl = new CountImpl();  
 8         CountProxy countProxy = new CountProxy(countImpl);  
 9         countProxy.updateCount();  
10         countProxy.queryCount();  
11     }  
12 }  

  以上静态代理的代码结合前面的结构图和UML图,相信不难理解代理模式的基本原理。

JDK动态代理

  为了提升代理的灵活性和可扩展性,减小重复代码,咱们可使用JDK提供的动态代理。

实现JDK动态代理的步骤:
  1. 经过实现 InvocationHandler 接口建立本身的调用处理器; 
  2. 经过为 Proxy 类指定 ClassLoader 对象和一组 interface 来建立动态代理类; 
  3. 经过反射机制得到动态代理类的构造函数,其惟一参数类型是调用处理器接口类型; 
  4. 经过构造函数建立动态代理类实例,构造时调用处理器对象做为参数被传入。
// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
// 其内部一般包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用
InvocationHandler handler = new InvocationHandlerImpl(..);

// 经过 Proxy 为包括 Interface 接口在内的一组接口动态建立代理类的类对象
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });

// 经过反射从生成的类对象得到构造函数对象
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });

// 经过构造函数对象建立动态代理类实例
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });

  实际使用过程更加简单,由于 Proxy 的静态方法 newProxyInstance 已经为咱们封装了步骤 2 到步骤 4 的过程:

// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
InvocationHandler handler = new InvocationHandlerImpl(..);

// 经过 Proxy 直接建立动态代理类实例
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader,
                                                     new Class[] { Interface.class },
                                                     handler );

JAVA示例代码:

public class TraceHandler implements InvocationHandler{
   private Object target = null;
   public TraceHandler(Object t) {
      this.target = t;
   }
 
   @Override
   public Object invoke(Object proxy, Method method, Object[] args)
       throws Throwable {
      System.out.print(target);
      System.out.print("." + method.getName() + "(");
      if(args != null) {
         for(int i = 0; i < args.length; ++i) {
            System.out.print(args[i]);
            if(i < args.length-1) System.out.print(", ");
         }
      }
      System.out.println(")");
      return method.invoke(target, args);
   }
}

Test.java

public void test() {
    Object[] elements = new Object[1000];
    for (int i = 0; i < elements.length; i++) {
       Integer val = i+1;
       TraceHandler handler = new TraceHandler(val);
       elements[i] = Proxy.newProxyInstance(null, new Class[]{Comparable.class}, handler);
    }
 
    Integer key = new Random().nextInt(1000) + 1;
    int result = Arrays.binarySearch(elements, key);
    if (result > 0) {
       System.out.println(elements[result]);
    }
 }

   Proxy 静态方法生成动态代理类一样须要经过类装载器来进行装载才能使用,它与普通类的惟一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。每次生成动态代理类对象时都须要指定一个类装载器对象。

动态生成的代理类自己的一些特色:

  1. 包:若是所代理的接口都是 public 的,那么它将被定义在顶层包(即包路径为空),若是所代理的接口中有非 public 的接口(由于接口不能被定义为 protect 或 private,因此除 public 以外就是默认的 package 访问级别),那么它将被定义在该接口所在包(假设代理了 com.ibm.developerworks 包中的某非 public 接口 A,那么新生成的代理类所在的包就是 com.ibm.developerworks),这样设计的目的是为了最大程度的保证动态代理类不会由于包管理的问题而没法被成功定义并访问;
  2. 类修饰符:该代理类具备 final 和 public 修饰符,意味着它能够被全部的类访问,可是不能被再度继承;
  3. 类名:格式是“$ProxyN”,其中 N 是一个逐一递增的阿拉伯数字,表明 Proxy 类第 N 次生成的动态代理类,值得注意的一点是,并非每次调用 Proxy 的静态方法建立动态代理类都会使得 N 值增长,缘由是若是对同一组接口(包括接口排列的顺序相同)试图重复建立动态代理类,它会很聪明地返回先前已经建立好的代理类的类对象,而不会再尝试去建立一个全新的代理类,这样能够节省没必要要的代码重复生成,提升了代理类的建立效率。
代理类实例的一特色:       
       在代理类实例上调用其代理的接口中所声明的方法时,这些方法最终都会由调用处理器的 invoke 方法执行,此外,值得注意的是,代理类的根类 java.lang.Object 中有三个方法也一样会被分派到调用处理器的 invoke 方法执行,它们是 hashCode,equals 和 toString。
       当代理的一组接口有重复声明的方法且该方法被调用时,代理类老是从排在最前面的接口中获取方法对象并分派给调用处理器,而不管代理类实例是否正在以该接口(或继承于该接口的某子接口)的形式被外部引用,由于在代理类内部没法区分其当前的被引用类型。

被代理的接口的特色:

  1. 要注意不能有重复的接口,以免动态代理类代码生成时的编译错误。
  2. 这些接口对于类装载器必须可见,不然类装载器将没法连接它们,将会致使类定义失败。
  3. 需被代理的全部非 public 的接口必须在同一个包中,不然代理类生成也会失败。
  4. 接口的数目不能超过 65535,这是 JVM 设定的限制。

异常处理的特色:

  代理类并不能抛出全部的异常,由于子类覆盖父类或实现父接口的方法时,抛出的异常必须在原方法支持的异常列表以内。
      若是代理产生了接口方法中不支持的异常,它将会抛出 UndeclaredThrowableException 异常。这个异常是一个 RuntimeException 类型,因此不会引发编译错误。经过该异常的 getCause 方法,还能够得到原来那个不受支持的异常对象,以便于错误诊断。

基于CGLIB的动态代理

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

CGlib概述:

  • cglib(Code Generation Library)是一个强大的,高性能,高质量的Code生成类库。它能够在运行期扩展Java类与实现Java接口。
  • cglib封装了asm,能够在运行期动态生成新的class。
  • cglib用于AOP,jdk中的proxy必须基于接口,cglib却没有这个限制。

  JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现加强,但由于采用的是继承,因此不能对final修饰的类进行代理。 很少说,直接上代码!

有一个Manager类:

public class Manager {

    public void query() {
        System.out.println("query...");
    }
    
    public void insert() {
        System.out.println("insert...");
    }
    
    public String update() {
        System.out.println("update....");
        return "I'm update";
    }
    
    public void delete() {
        System.out.println("delete....");
    }
}

咱们想要在这个类中的每一个方法前面和后面都打印一句话,这时候咱们就可使用代理了,让咱们来看一下这个代理能够怎么写:

public class AuthProxy implements MethodInterceptor{
    
    @Override
    public Object intercept(Object arg0, Method method, Object[] args,
            MethodProxy proxy) throws Throwable {
        System.out.println("Before...");
        Object result = proxy.invokeSuper(arg0, args);
        System.out.println("After....");
        return result;
    }
}

  如上,CGLIB实现的代理类必须实现MethodInterceptor接口,该接口中只有一个方法须要实现,即intercept方法。经过MethodProxy中的invokeSuper便可执行被代理类的方法。咱们继续往下看。

public static Manager getInstace(AuthProxy auth) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Manager.class);
        enhancer.setCallback(auth);
        return (Manager) enhancer.create();
    }

  经过以上代码咱们就能获得一个Manager的代理类,被AuthProxy代理。

public void AuthProxyTest() {
        AuthProxy auth = new AuthProxy();
        Manager manager = ManagerFactory.getInstace(auth);
        manager.delete();
        System.out.println();
        manager.query();
        System.out.println();
        String result = manager.update();
        System.out.println("result: " + result);
    }

下面是一个能够代理不一样类的代理生成工厂:

public class ManagerFactory2 {
    
    public static Manager getInstace(Class clasz, AuthProxy auth) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clasz);
        enhancer.setCallback(auth);
        return (Manager) enhancer.create();
    }
}

对这个工厂进行通用化扩展:

public class ManagerFactory {
    
    public static Manager getInstace(Class clasz, AuthProxyFilter filter, AuthProxy auth, Object...args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clasz);
        
        Callback[] callback = new Callback[args.length+1];
        System.out.println("length: " + callback.length);
        callback[0] = auth;
        for (int i = 0; i < args.length; i++) {
            if (args[i] instanceof Callback) {
                callback[i+1] = (Callback) args[i];
            }else {
                callback[i+1] = NoOp.INSTANCE;
            }
        }
        
        enhancer.setCallbacks(callback);
        enhancer.setCallbackFilter(filter);
        return (Manager) enhancer.create();
    }
AuthProxyFilter.java
public class AuthProxyFilter implements CallbackFilter{

    @Override
    public int accept(Method method) {
        if ("query".equals(method.getName())) {
            return 1;
        }
        return 0;
    }
}

 =====================华丽的分割线==================================

                                              源码请猛戳{ 这里

===============================================================

参考资料:

http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html

http://www.blogjava.net/stone2083/archive/2008/03/16/186615.html

相关文章
相关标签/搜索