Java动态代理探讨

代理模式:

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

UML图:

简单结构示意图:

  为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。

按照代理的创建时期,代理类可以分为两种:

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

静态代理:

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

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

时间: 2024-10-09 20:49:30

Java动态代理探讨的相关文章

java动态代理【一】

java动态代理的定义:为其他目标类的方法增加切面的逻辑,即在执行目标类方法的时候,先去执行一段如校验检测的逻辑代码.java通俗一点就是生成一个继承目标类的子类,并在每个调用方法都添加一段逻辑. 应用场景:当我们从别的项目迁移过来的代码进行修改的时候,如果有一个需求是当要执行某个业务类的所有方法前,需要校验其权限或其他的时候,如果这个类是源代码,我们还可以在类的基础上对每个方法区更改,但若是打包成jar包的类,若该类有接口还可以实现一个代理模式创建一个代理类,没有接口就比较麻烦,但接口一旦多起

理解java动态代理

java动态代理是java语言的一项高级特性.在平时的项目开发中,可能很难遇到动态代理的案例.但是动态代理在很多框架中起着不可替代的作用,例如Spring的AOP.今天我们就聊一聊java动态代理的实现原理. jdk对于动态代理的支持主要依赖于两个类:Proxy和InvocationHandler.我们先看一下类图. Subject类是主题类,定义了我要做什么.我们需要代理的类即实现Subject接口的RealSubject. 1.InvocationHandler InvocationHand

java动态代理的实现

动态代理作为代理模式的一种扩展形式,广泛应用于框架(尤其是基于AOP的框架)的设计与开发,本文将通过实例来讲解Java动态代理的实现过程. 友情提示:本文略有难度,读者需具备代理模式相关基础知识,. 通常情况下,代理模式中的每一个代理类在编译之后都会生成一个class文件,代理类所实现的接口和所代理的方法都被固定,这种代理被称之为静态代理(Static Proxy).那么有没有一种机制能够让系统在运行时动态创建代理类?答案就是本文将要介绍的动态代理(Dynamic Proxy).动态代理是一种较

java动态代理机制

首先了解代理设计模式,其思想是为其他对象提供一种代理以控制对这个对象的访问. java动态代理就是遵循这种思想,spring中的AOP实现原理就是java的动态代理. 在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface).另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的. 每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通

java动态代理

要想了解Java动态代理,首先要了解什么叫做代理,熟悉设计模式的朋友一定知道在Gof总结的23种设计模式中,有一种叫做代理(Proxy)的对象结构型模式,动态代理中的代理,指的就是这种设计模式. 在我看来所谓的代理模式,和23种设计模式中的"装饰模式"是一个东西.23种设计模式中将它们作为两种模式,网上也有些文章讲这两种模式的异同,从细节来看,确实可以人为地区分这两种模式,但是抽象到一定高度后,我认为这两种模式是完全一样的.因此学会了代理模式,也就同时掌握了装饰模式. 代理模式 代理模

java 动态代理示例,带主要注释

Java proxy是基于反射,仅仅支持基于接口的动态代理. java 动态代理是一切架构的基础,必须了解. 废话少说,先上代码获得感性认识. 示例代码有主要注释. 接口: public interface Subject { String hello(String name); void say();} 接口实现: public class ImpSubject implements Subject { @Override public String hello(String name){ r

java动态代理原理及解析

代理:设计模式 代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个真实对象的访问.代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理. 通过代理层这一中间层,有效的控制对于真实委托类对象的直接访问,同时可以实现自定义的控制策略(Spring的AOP机制),设计上获得更大的灵活性. java动态代理的类和接口(jdk1.6源码) 1,java.lang.reflect.Proxy:动态代理机制的主类,提供一组静态方法为一组接口动态的生成对

彻底理解JAVA动态代理

注:本文转自 http://www.cnblogs.com/flyoung2008/archive/2013/08/11/3251148.html 代理设计模式 定义:为其他对象提供一种代理以控制对这个对象的访问. 代理模式的结构如下图所示. 动态代理使用 java动态代理机制以巧妙的方式实现了代理模式的设计理念. 代理模式示例代码 public interface Subject { public void doSomething(); } public class RealSubject i

JAVA动态代理详解

1.什么是代理 代理模式是常用的Java 设计模式,它的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等. 2.什么是动态代理 在程序运行时,运用反射机制动态创建代理实例对象.JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理. 相关类与接口 java.lang.reflect.Proxy:这是 Java 动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象. //