`

读JDK动态代理源码(Proxy类)

    博客分类:
  • Java
 
阅读更多

读源码,怕过两天又忘记了,还是记录下吧..

动态代理最重要的实现就是Proxy.newInstance ,那我们直接看这个方法

public static Object newProxyInstance(ClassLoader loader,
       Class<?>[] interfaces,
       InvocationHandler h)//这里有三个参数,第一个是传入classloader,一般情况是传入当前的classloader.但是我在上一节模拟实现里传入的是URL loader..第二个参数表示的是接口,第三个是Invocationhandler,除了第一个参数,其他和我在上一节里的一样.JDK的封装的比较好,所以他传入的是Interface的数组,
 throws IllegalArgumentException
    {
 if (h == null) {
     throw new NullPointerException();//如果Invocationhandler 为空,抛异常
 }

 
 Class cl = getProxyClass(loader, interfaces);

 
 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());
 }
    }

上面的方法,除了最重要的getProxyClass,其他都很容易理解.,那么下面开始读getProxyClass方法

public static Class<?> getProxyClass(ClassLoader loader,
                                         Class<?>... interfaces)
 throws IllegalArgumentException
    {
 if (interfaces.length > 65535) {
     throw new IllegalArgumentException("interface limit exceeded");//JDK想的果然比较到位,连interface传的太多都想到了..~!~
 }

 Class proxyClass = null;//这个就是最后要生成的二进制码,首先初始化一下

 
 String[] interfaceNames = new String[interfaces.length];//这个存放的是对应的Interface的名字..

 Set interfaceSet = new HashSet(); //这个HashSet 是为了检测interface重复记录的.

 for (int i = 0; i < interfaces.length; i++) {
    
     String interfaceName = interfaces[i].getName();
     Class interfaceClass = null;
     try {
  interfaceClass = Class.forName(interfaceName, false, loader);//创建对应接口的二进制码,第二个参数false表示,不需要初始化
     } catch (ClassNotFoundException e) {
     }
     if (interfaceClass != interfaces[i]) {//如果创建出来的二进制码和原来的接口不一样,表示这个接口对于这个classloader来说是不可见的
  throw new IllegalArgumentException(
      interfaces[i] + " is not visible from class loader");
     }

    
     if (!interfaceClass.isInterface()) {//如果不是接口,那么就抛异常,这里就规定了,我们必须通过接口来代理..或者说,必须面向接口编程
  throw new IllegalArgumentException(
      interfaceClass.getName() + " is not an interface");
     }

    
     if (interfaceSet.contains(interfaceClass)) {
  throw new IllegalArgumentException(
      "repeated interface: " + interfaceClass.getName());
     }
     interfaceSet.add(interfaceClass);

     interfaceNames[i] = interfaceName;//这句就是把每个接口名放到interfaceNames数组里..
 }

 
 Object key = Arrays.asList(interfaceNames);//这里把Interface数组转换成list..这里直接写成了Object类型

 
 Map cache;//放缓存的Map
 synchronized (loaderToCache) {//loaderToCache也是一个Map.它的key是classloader,对应的value是对应的缓存,也是一个HashMap.他把对应的不同的classloader放到loaderToCache里,如果下次还要调用这个方法创建代理,并传入的是同一个classloader,那么可以直接从cache里取..增加速度.当然,如果没有,则创建一条记录,放到loaderToCache里
     cache = (Map) loaderToCache.get(loader);
     if (cache == null) {
  cache = new HashMap();
  loaderToCache.put(loader, cache);
     }
    
 }

 
 synchronized (cache) {
    
     do {
  Object value = cache.get(key);//这里从cache里获取对应的Object..第一次执行的话,明显获取到的是Null..key表示的是用接口名字转换而来的list..这个可以看上面的代码.
  if (value instanceof Reference) {
      proxyClass = (Class) ((Reference) value).get();//如果已经能获取到了,那么,我们需要的二进制码文件就是这个获取到的
  }
  if (proxyClass != null) {
      // proxy class already generated: return it
      return proxyClass;
  } else if (value == pendingGenerationMarker) {//这里的pendingGenerationMarker是一个静态常量,表示 new Object().JDK给出的 解释是,如果代理正在创建,那么等待他
      // proxy class being generated: wait for it
      try {
   cache.wait();
      } catch (InterruptedException e) {
   
      }
      continue;
  } else {
     
      cache.put(key, pendingGenerationMarker);//如果cache里获取到的对应于key的value是Null ,那么,就创建一个object对象放进去.上面说了,pendingGenerationMarker= new Object();
      break;
  }
     } while (true);
 }

 try {
     String proxyPkg = null; // package to define proxy class in 这个是代理类的包名

    
     for (int i = 0; i < interfaces.length; i++) {
  int flags = interfaces[i].getModifiers();//getModifiers()方法返回的是该接口的修饰类型,用Int类型表示
  if (!Modifier.isPublic(flags)) {//如果不是public 的接口..

  //我们可以这样理解,我们动态创建的代理,他的修饰类型必须是和接口的修饰类型是一样的,我们知道,接口可以是public或者默认,两种修饰类型.这里的判断如果不是public接口,那么,该接口肯定是默认的,如果是默认修饰类型,那么,它只能被同一个包下面的类看到,所以,就必须为该代理类创建一个包名..当然,如果是public的话,就没必要,因为,反正所有的类都能看到..
      String name = interfaces[i].getName();
      int n = name.lastIndexOf('.');
      String pkg = ((n == -1) ? "" : name.substring(0, n + 1));//注意这里的N+1 ,其实是包括 "."的..比如 com.cjb.proxy.Proxy..它返回的就是"com.cjb.proxy."注意最后的那个点

      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,这里可以看到,如果是public 的接口,对应代理类的包名就是"",也就是没有包名
  proxyPkg = "";  // use the unnamed package
     }

     {
  
  long num;
  synchronized (nextUniqueNumberLock) {
      num = nextUniqueNumber++;//Num是一个计数器.用处是,创建代理的类名的时候用..我们可以看到,它是初始值是0.然后,每被调用一次,Num++.
  }
  String proxyName = proxyPkg + proxyClassNamePrefix + num;//proxyPkg是之前生成的包名, proxyClassNamePrefix 是一个静态常量,proxyClassNamePrefix="$Proxy".最后Num是计数器.也就是说,它的代理类的名字是从 $Proxy1 $Proxy2 $Proxy3一直在增长的,这样的话,就避免了重复.
  

  
  byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
      proxyName, interfaces);
  try {
      proxyClass = defineClass0(loader, proxyName,
   proxyClassFile, 0, proxyClassFile.length);

  } catch (ClassFormatError e) {
     
      throw new IllegalArgumentException(e.toString());
  }
     }
     // add to set of all generated proxy classes, for isProxyClass
     proxyClasses.put(proxyClass, null);

 } finally {
    
     synchronized (cache) {
  if (proxyClass != null) {
      cache.put(key, new WeakReference(proxyClass));
  } else {
      cache.remove(key);
  }
  cache.notifyAll();
     }
 }
 return proxyClass;
    }

可以看到,我们想看的,最重要的生成二进制码的方法,是native的..读了这么多源码,都是一些前期处理.关键的地方不知道..当然,虽然没看到关键的地方,但是对于它前面的处理的学习,也是非常有用的..看看JDK是怎么处理重名问题的,怎么处理cache.等等..

分享到:
评论

相关推荐

    面试官:你说你懂动态代理,那你知道为什么JDK中的代理类都要继承Proxy吗?

    在动态代理学习(二)JDK动态代理源码分析中我已经讲JDK底层生成的字节码文件反编译成了java代码,如下: public final class proxy extends Proxy implements MyService { private static Method m1; private ...

    java设计模式【之】JDK动态代理【源码】【场景:帮爸爸买菜】.rar

    java设计模式【之】JDK动态代理【源码】【场景:帮爸爸买菜】.rar /** * 代理模式 * 在开发者的角度来看,创建一个代理对象,提供给用户使用,避免用户直接访问真正的对象 * 在用户角度来看,就是普通的类方法...

    JDK的动态代理(powernode 文档)(源代码)

    JDK的动态代理(powernode 文档)(源代码) JDK的动态代理(powernode 文档) 一、动态代理 1.1JDK动态代理 1.1.1 proxy 1.1.2 InvocationHandler 1.1.3 创建一个Maven项目 1.1.4 导入Spring的相关依赖 1.1.5 修改...

    两万字吐血总结,代理模式及手写实现动态代理(aop原理,基于jdk动态代理)

    jdk动态代理源码分析(通过该示例学会阅读源码的方法)2.jdk动态代理生成的代理类的源码3.总结三、手写实现jdk动态代理 一、代理模式 熟悉代理模式的可以直接点击目录第二章,jdk动态代理实现原理,本文的精髓所在,...

    JDK动态代理在EJB3(包括WebService)中的应用

    NULL 博文链接:https://f543711700.iteye.com/blog/1172586

    Jdk动态代理 底层

    当spring没有手动开启Cglib动态代理,即:或@EnableAspectJAutoProxy(proxyTargetClass = true),默认使用的就是Jdk动态代理。动态代理的应用范围很广,例如:日志、事务管理、缓存等。本文将模拟@Cacheable,即缓...

    java常用设计模式及JDK与CGLIB实现动态代理区别(源码)

    java常用设计模式及JDK与CGLIB实现动态代理区别(源码) /** * 使用cglib动态代理 * @author * */ public class BookFacadeProxy implements MethodInterceptor{ private Object target; @Override public...

    你必须会的 JDK 动态代理和 CGLIB 动态代理

    我们在阅读一些 Java 框架的源码时,基本上常会看到使用动态代理机制,它可以无感的对既有代码进行方法的增强,使得代码拥有更好的拓展性。 通过从静态代理、JDK 动态代理、CGLIB 动态代理来进行本文的分析。 静态...

    Java 动态代理.md

    - JDK 动态代理 - CGLIB 动态代理 - Javassist 代理 - ASM 代理 这篇文章我们来聊一下 Java 中的动态代理。 动态代理在 Java 中有着广泛的应用,比如 AOP 的实现原理、RPC远程调用、Java 注解对象获取、日志...

    动态代理和AOP详解

    本资源利用文档加源码的方式较详细的介绍了JDK 动态代理和Spring的AOP机制.相信对您理解相关方面的知识有很大作用.

    java8看不到源码-HTTP-Proxy:HTTP/HTTPS(TunnelingoverHTTP)使用Java实现的转发代理

    JDK 1.8 或更高版本。 基于蔚来 概述 nio-http-proxy包含基于 NIO 的 HTTP/HTTPS 转发代理实现,它是健壮的和 CPU 内存高效的。 快速开始 此 repo 带有存储在bin/lib/nio-http-roxy.jar因此您可以简单地运行代理 ...

    JDK1.6使用BouncyCastle第三方jar支持TSL1.2通过Nginx转发多个HTTPS接口请求

    应用从DMZ迁移到内网后,就不能连接外网了,必须通过代理服务器才能调用第三方接口。使用Nginx作为代理服务器,HTTP的接口代理没有...\doc\proxy:Nginx的一些源码包,Haproxy源码包,安装步骤可参考readme.txt文件。

    AOP__使用JDK中的Proxy技术实现AOP功能

    NULL 博文链接:https://countme.iteye.com/blog/1130188

    proxy-project.rar

    JDK动态代理例子,idea下的项目,直接打开可用

    java6string源码-dynamic-proxy:利用ASM、CGLIB、ByteBuddy、Javassist和JDKDynamicP

    java6string源码Java 动态代理 动态代理是 Java 开发人员生成代理对象的有用库。 该库利用了广泛的字节码生成方法,包括 1. 指南 1.1 先决条件 Maven 依赖: &lt;groupId&gt;net.neoremind&lt;/groupId&gt; &lt;artifactId&gt;dynamic...

    jdk1.8-source:JDK1.8源码分析包

    JDK1.8源码分析 相关的原始码分析结果会以注解的形式体现到原始码中 已完成部分: ReentrantLock CountDownLatch Semaphore HashMap TreeMap LinkedHashMap ConcurrentHashMap 执行器 ...

    java8源码-javase:Java

    用到了Jdk动态代理,以及Classloader。 resource 该模块主要用于Class和Classloader如何获取资源。 stream 用于JDK8的stream api用法。 string 用于写String类的用法。 iText-demo 主要来自 Jackson 使用Jackson和...

    Spring.html

    JDK动态代理(默认) 基于接口:代理对象与目标对象是兄弟关系,目标类必须实现接口 CGLIB动态代理 基于父类:代理对象与目标对象是父子关系.目标不能被final修饰 修改默认代理方法:&lt;aop:aspectj-autoproxy ...

    疯狂JAVA讲义

    学生提问:既然内部类是外部类的成员,是否可以为外部类定义子类,在子类中再定义一个内部类来重写其父类中的内部类? 211 6.7.4 局部内部类 211 6.7.5 匿名内部类 212 6.7.6 闭包(Closure)和回调 215 6.8 ...

    2023最新飞机即时通讯系统源码 PC+Android+IOS+WEB四端

    2023最新飞机即时通讯系统源码 PC+Android+IOS+WEB四端 环境Nginx+mysql5.7+php7.3+redis+jdk1.8+etcd、kafka、minio、ssdb 数据库imweb、enterprise、databus web 伪静态 code location / { if (!-e $request_...

Global site tag (gtag.js) - Google Analytics