`
Mysun
  • 浏览: 273171 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

JDK 1.5 Proxy 动态代理机制分析

阅读更多
Java的动态代理机制早在其1.3版本就已经引入了。在jdk1.5中,动态代理机制主要由两个类来实现,他们是Proxy类和InvocationHandler接口,他们都可以在java.lang.reflect包下面找到。
InvocationHandler接口里面只有一个方法invoke,为了使用的java的动态代理,用户需要实现这个接口,并且在invoke方法中写好调用实际方法的代码,同时还能在调用实际方法的前后加上其他的逻辑,比如日志或者事物操作等等。这个接口的实现类最终会被由jdk生成的动态代理类使用来调用实际的方法。关于如何使用java动态代理,请参阅网上其它文章,下面将要就Proxy生成动态代理类的源码进行分析。
在Proxy类里面,有两个方法跟动态代理类的生成有关,他们是:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,
InvocationHandler h) throws IllegalArgumentException;

public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
throws IllegalArgumentException

第一个方法比较简单,代码如下:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
throws IllegalArgumentException{
   if (h == null) {
      throw new NullPointerException();
   }
   /*
    * Look up or generate the designated proxy class.
    */
   Class cl = getProxyClass(loader, interfaces);
   /*
    * Invoke its constructor with the designated invocation handler.
    */
   try {
      Constructor cons = cl.getConstructor(constructorParams);
      return (Object) cons.newInstance(new Object[] { h });
   } catch (NoSuchMethodException e) {
      throw new InternalError(e.toString());
   } catch (IllegalAccessException e) {
      throw new InternalError(e.toString());
   } catch (InstantiationException e) {
      throw new InternalError(e.toString());
   } catch (InvocationTargetException e) {
      throw new InternalError(e.toString());
   }
}

可以看到,在newProxyInstance方法内部调用了getProxyClass方法,然后使用反射机制来创建由getProxyClass返回的动态代理类的实例。所以让我们重点来看一下getProxyClass这个方法。首先要说明一下在getProxyClass里面用到的几个类私有变量的作用。
/** maps a class loader to the proxy class cache for that loader */
private static Map loaderToCache = new WeakHashMap();
这个变量用来保存某个类加载器以及使用该加载器加载的动态代理类集合。key为类加载器,
value则是一个HashMap,里面放了具体的动态代理类,和用来索引的key。使用WeakHashMap是处于性能的考虑
/** marks that a particular proxy class is currently being generated */
private static Object pendingGenerationMarker = new Object();
用来标识一个动态代理类正在被创建,主要用来处理多线程的情况
/** next number to use for generation of unique proxy class names */
private static long nextUniqueNumber = 0;//用来生成唯一的动态代理类名时候用到
private static Object nextUniqueNumberLock = new Object();//锁对象
/** set of all generated proxy classes, for isProxyClass implementation */
private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap());
这个变量用来保存所有生成的所有动态代理类,主要是为了方便另外一个方法isProxyClass的使用设置的,在getProxyClass这个方法里面是没有什么用处的。同样适用的WeakHashMap,也是出于性能的考虑,不至于因为proxyClasses持有动态代理类的应用而导致垃圾回收器没法回收不再使用的动态代理类。

好了,介绍了变量,我们可以来关注一下getProxyClass方法了,这个方法大致可以分为三步,第一步是把传入的interface类的名字保存下来,第二步检查要用户请求的动态代理类是否已经存在了,如果已经存在那么就直接返回之前创建好的动态代理类。第三步就是当用户请求的动态代理类不存在的时候去创建这个动态代理类。让我们分别看一下这几部分的代码。
[list]
  • 第一步:
  •        /* collect interface names to use as key for proxy class cache */
    	String[] interfaceNames = new String[interfaces.length];
    
    	for (int i = 0; i < interfaces.length; i++) {
    	    /*
    	     * Verify that the class loader resolves the name of this
    	     * interface to the same Class object.
    	     */
    	    String interfaceName = interfaces[i].getName();
    	    Class interfaceClass = null;
    	    try {
    		interfaceClass = Class.forName(interfaceName, false, loader);
    	    } catch (ClassNotFoundException e) {
    	    }
    	    if (interfaceClass != interfaces[i]) {
    		throw new IllegalArgumentException(
    		    interfaces[i] + " is not visible from class loader");
    	    }
    
    	    /*
    	     * Verify that the Class object actually represents an
    	     * interface.
    	     */
    	    if (!interfaceClass.isInterface()) {
    		throw new IllegalArgumentException(
    		    interfaceClass.getName() + " is not an interface");
    	    }
    
    	    interfaceNames[i] = interfaceName;
    	}
    

    这一步比较好理解,就是获得各个interface类的名字,然后保存到一个字符串数组中。其中做了好几步的检查。需要说明的是,这个生成数组将会被用来作为索引动态代理类的key。
  • 第二步:
  •         //将之前生成的接口名字数组转成List,用来作为基于这组接口生成的动态代理
            //类的索引
            Object key = Arrays.asList(interfaceNames);
            /*
    	 * 检查对于作为参数输入的类加载器是否已经创建过了动态代理类缓存,如果没有
             * 就创建一个,如果有就取出来。
    	 */
    	Map cache;
    	synchronized (loaderToCache) {
    	    cache = (Map) loaderToCache.get(loader);
    	    if (cache == null) {
    		cache = new HashMap();
    		loaderToCache.put(loader, cache);
    	    }
               //下面这段话说明了使用WeakHashMap的好处
    	    /*
    	     * This mapping will remain valid for the duration of this
    	     * method, without further synchronization, because the mapping
    	     * will only be removed if the class loader becomes unreachable.
    	     */
    	}
            //下面这段代码用来检查实现了指定接口的动态代理是否已经被创建过了。
            synchronized (cache) {
                do{
                    //尝试用key从动态代理cache中取动态代理
    		Object value = cache.get(key);
    		if (value instanceof Reference) {
    		   proxyClass = (Class) ((Reference) value).get();
    		}
                    //如果动态代理已经存在,那么就直接返回找到的动态代理
    		if (proxyClass != null) {
    		// proxy class already generated: return it
    		   return proxyClass;
    		}
                    //如果没有找到动态代理,但是发现key对应的是一个
                    //pendingGenerationMarker对象,那就说面之前已经有别的线程已经在
                    //创建这个动态代理了,所以进入等待状态。负责创建的动态代理的
                    //那个线程创建完动态代理之后就会notify所有在cache上wait的线程
                    //这些线程被激活后就会继续执行do循环,然后发现动态代理已经被创建
                    //了,所以就直接返回被创建了动态代理
                    else if (value == pendingGenerationMarker) {
    		// proxy class being generated: wait for it
    		   try {
    			cache.wait();
    		   } catch (InterruptedException e) {
    		  /*
    	           * The class generation that we are waiting for should
    		   * take a small, bounded time, so we can safely ignore
    		   * thread interrupts here.
    		   */
    		   }
    			continue;
    		   }
                       //如果发现自己是第一个要创建动态代理的线程,就在对应的key上
                       //放置pendingGenerationMarker标志对象
                       else {
    		   /*
    		    * No proxy class for this list of interfaces has been
    		    * generated or is being generated, so we will go and
    		    * generate it now.  Mark it as pending generation.
                        */
    			cache.put(key, pendingGenerationMarker);
    			 break;
    		     }
    	    } while (true);
    	}
    
  • 第三步:创建动态代理
  •         try {
                //在动态代理的接口不是public的情况下,保存动态代理所在的包
    	    String proxyPkg = null;	// package to define proxy class in
    
    	    /*
    	     * Record the package of a non-public proxy interface so that the
    	     * proxy class will be defined in the same package.  Verify that
    	     * all non-public proxy interfaces are in the same package.
    	     */
                //在动态代理的接口不是public的情况下,找出动态代理应该被创建在哪个包中
                //如果出现两个不同的包的非public Inteface就抛错
    	    for (int i = 0; i < interfaces.length; i++) {
    		int flags = interfaces[i].getModifiers();
    		if (!Modifier.isPublic(flags)) {
    		    String name = interfaces[i].getName();
    		    int n = name.lastIndexOf('.');
    		    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
    		    if (proxyPkg == null) {
    			proxyPkg = pkg;
    		    } 
                        else if (!pkg.equals(proxyPkg)) 
                        {
    		      throw new IllegalArgumentException(
    			    "non-public interfaces from different packages");
    		     }
    		 }
    	    }
    	    if (proxyPkg == null) {	// if no non-public proxy interfaces,
    		proxyPkg = "";		// use the unnamed package
    	    }
    
    	    {
    		/*
    		 * Choose a name for the proxy class to generate.
    		 */
                    //去一个数字,用来作为生成的动态代理的一部分
    		long num;
    		synchronized (nextUniqueNumberLock) {
    		    num = nextUniqueNumber++;
    		}
                    //动态代理类的名字,proxyClassNamePrefix是固定的,值为"$Proxy"
                    //java中合法的类名和变量名可以以$符开头
    		String proxyName = proxyPkg + proxyClassNamePrefix + num;
    		/*
    		 * Verify that the class loader hasn't already
    		 * defined a class with the chosen name.
    		 */
    
    		/*
    		 * Generate the specified proxy class.
    		 */
                    //下面就是创建动态代理的类文件,然后把生成的动态代理类加载进
                    //JVM中,下面用到的几个方法是java语言比较低层的机制,这里就
                    //不说了,有兴趣的可以看看源代码
    		byte[] proxyClassFile =	ProxyGenerator.generateProxyClass(
    		    proxyName, interfaces);
    		try {
    		    proxyClass = defineClass0(loader, proxyName,
    			proxyClassFile, 0, proxyClassFile.length);
    		} catch (ClassFormatError e) {
    		    /*
    		     * A ClassFormatError here means that (barring bugs in the
    		     * proxy class generation code) there was some other
    		     * invalid aspect of the arguments supplied to the proxy
    		     * class creation (such as virtual machine limitations
    		     * exceeded).
    		     */
    		    throw new IllegalArgumentException(e.toString());
    		}
    	    }
    	    // add to set of all generated proxy classes, for isProxyClass
    	    proxyClasses.put(proxyClass, null);
    
    	} finally {
    	    /*
    	     * We must clean up the "pending generation" state of the proxy
    	     * class cache entry somehow.  If a proxy class was successfully
    	     * generated, store it in the cache (with a weak reference);
    	     * otherwise, remove the reserved entry.  In all cases, notify
    	     * all waiters on reserved entries in this cache.
    	     */
                //这里是用来擦屁股的,首先把生成的动态代理类加到对应的cache里面,然后
                //去激活所有被同一个cache阻塞的线程,通知他们动态代理已经生成的,好让 
                //他们继续执行。
                //如果动态代理因为各种情况没有生成成功,那么就把cache里面的key删除掉
    	    synchronized (cache) {
    		if (proxyClass != null) {
    		    cache.put(key, new WeakReference(proxyClass));
    		} else {
    		    cache.remove(key);
    		}
    		cache.notifyAll();
    	    }
    	}
            return proxyClass;
    

    [/list]

    分享到:
    评论
    1 楼 endall 2011-03-31  
    不错,结合Proxy原码,再看这篇文章,易懂

    相关推荐

      JDK1.6、JDK1.4 API和MySQL

      3. **动态代理**:增强了`java.lang.reflect.Proxy`类,使得创建动态代理对象更加简单。 4. **Swing增强**:包括更好的外观和感觉,以及更多的组件和事件监听器。 5. **XML处理**:对DOM、SAX和StAX的API进行了改进...

      JDK1.6.0_13免安装版

      4. **动态代理**:在JDK 1.6中,Java引入了Java.lang.reflect.Proxy类,允许创建动态代理对象,这对于实现AOP(面向切面编程)等设计模式非常有用。 5. **NIO.2**:Java NIO (Non-blocking Input/Output) 在1.6版本...

      JDK_API_1_7_zh_CN.zip

      7. **动态代理**:`java.lang.reflect.Proxy`类提供了创建动态代理的能力,可以在运行时生成符合指定接口的新类。 8. **注解**:`java.lang.annotation`包中的注解(Annotation)可以用来添加元数据到代码中,增强...

      jdk1.6帮助文档

      这种动态代理机制常用于AOP(面向切面编程)和测试框架中。 ### 9. 安全性更新 JDK 1.6的安全模型得到了加强,包括新的沙箱机制和证书管理,以应对日益复杂的网络安全威胁。 ### 10. 性能优化 JDK 1.6对JVM进行...

      JDK_API_1_6_zh_CN_downcc.com.zip

      Java 1.6通过`java.lang.reflect.Proxy`和`java.lang.reflect.InvocationHandler`支持动态代理,可以在运行时创建符合特定接口的新类。 6. **NIO(非阻塞I/O)**: 引入`java.nio`包,提供了基于通道(Channel)...

      JDK_API_1_6_zh_CN.chm

      5. **动态代理**:java.lang.reflect包中的Proxy类和InvocationHandler接口使得动态创建代理对象变得更加容易,便于实现AOP(面向切面编程)或其他拦截器模式。 6. **改进的内存管理**:JVM的垃圾收集机制得到优化...

      win64位jdk1.6

      4. **动态代理**:通过`java.lang.reflect.Proxy`类,开发者可以创建动态实现接口的代理对象,用于实现AOP(面向切面编程)和其他设计模式。 5. **改进的并发库**:JDK1.6提供了更强大的并发工具,如`java.util....

      JDK_API_zh_CN.CHM

      动态代理(Dynamic Proxy)允许在运行时创建符合特定接口的新类,用于实现AOP(面向切面编程)等场景。 9. **并发编程** `java.util.concurrent`包提供了高级并发工具,如线程池、并发容器、原子变量等,以支持...

      JDK_API_1_6_zh_CN

      8. **动态代理**:Java.lang.reflect包中的Proxy类和InvocationHandler接口,使得开发者可以在运行时创建动态代理类,实现对接口方法的拦截和自定义行为。 9. **泛型**:自JDK 5引入以来,泛型在1.6版本中得到了...

      jdk 1.6 带索引 api

      4. **动态代理增强**:Java.lang.reflect包中的Proxy类得到了改进,可以创建接口的动态代理类,使得在运行时动态地创建符合指定接口的类成为可能。 5. **并发编程的改进**:引入了`java.util.concurrent`包,提供了...

      linux下weblogic9.2集群安装部署借鉴.pdf

      * 例如,我们以两台 WEB 应用服务器配置的一个集群示例,server1 和 server2,server1 作为一个群集服务器节点,並将集群的管理器(adminserver)及代理分发服务(proxyserver)也配在 server1 上,server2 只作为...

      day3-springAOP.md

      **1.5 CGLIB 动态代理** - **目标类**:一个普通的类 `Target`,不实现任何接口。 - **动态代理代码**: - 创建目标对象; - 使用 CGLIB 的 `Enhancer` 类来创建代理对象; - 设置代理对象的父类为 `Target` 类...

      jdk-1.6api中文文档

      - **动态代理**:通过`java.lang.reflect.Proxy`和`java.lang.reflect.InvocationHandler`,实现了动态创建接口的代理对象,方便实现AOP(面向切面编程)等场景。 3. **JDK工具** - **javac**:Java源代码编译器...

      JDK_API_1_6_中文

      4. **动态代理**:`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口,允许创建动态实现接口的代理类,增强了设计模式中的代理模式的实现。 5. **NIO.2**:Java 7引入了NIO.2,但在1.6中...

      IDK API 1.6.0中文.rar

      2. **动态代理**:Java 1.6引入了`java.lang.reflect.Proxy`类,允许开发者创建动态的代理类,实现接口方法的动态调用,增强了代码的灵活性和可扩展性。 3. **改进的并发工具**:引入了`java.util.concurrent`包,...

      Java程序设计模式程序设计.doc

      Java 设计模式概述 Java 设计模式是软件设计中的一种解决方案,旨在提供通用的设计架构,以便...在 JDK1.5 之后引入了 Enum 枚举,因此在 JDK1.5 之后 Singleton 单类模式又有了第三种实现方式,也是最好的实现方式。

      java高新技术.txt

      这种特性自 JDK 1.5 起被引入。 #### 装箱 - **定义**: 将基本数据类型值转换为对应的包装类对象。 - **示例**: ```java Integer num1 = 3; // 将 int 类型的 3 装箱为 Integer 对象 ``` #### 拆箱 - **定义**:...

      JAVA反射侯捷观点

      本文适合具备Java语言基础的读者阅读,主要使用JDK1.5,并涉及关键词Introspection(内省)和Reflection(反射)。内省是指程序自我检查和理解的能力,而反射则是这一能力的具体实现,它使Java程序能够在运行时加载...

    Global site tag (gtag.js) - Google Analytics