什么是com.sun.proxy。$ Proxy

时间:2013-10-28 11:25:59

标签: java jpa proxy jvm

我已经看到,当错误发生在不同的框架(例如实现EJB规范的框架或一些JPA提供程序)时,堆栈跟踪包含类似com.sun.proxy.$Proxy的类。我知道什么是代理,但我正在寻找更具技术性和更具体的java答案。

  1. 他们是什么?
  2. 他们是如何创造的?
  3. 与JVM有什么关系?它们是JVM实现特定的吗?

2 个答案:

答案 0 :(得分:38)

他们是什么?

没什么特别的。与普通的Java Class Instance一样。

但是这些课程由java.lang.reflect.Proxy#newProxyInstance

创建Synthetic proxy classes

与JVM有什么关系?它们是JVM实现特定的吗?

在1.3中引入

http://docs.oracle.com/javase/1.3/docs/relnotes/features.html#reflection

它是Java的一部分。所以每个JVM都应该支持它。

他们是如何创建的(Openjdk7源码)?

简而言之:它们是使用JVM ASM技术创建的(在运行时定义javabyte代码)

使用相同技术的东西:

调用java.lang.reflect.Proxy#newProxyInstance

后会发生什么
  1. 阅读源代码,您可以看到newProxyInstance调用getProxyClass0来获取`类   

    `

  2. 经过大量缓存后,它会调用返回字节[]
  3. 的魔法ProxyGenerator.generateProxyClass
  4. 调用ClassLoader define class加载生成的$Proxy类(您看到的类名)
  5. 只是实例并准备好使用
  6. 魔法sun.misc.ProxyGenerator

    会发生什么
    1. 绘制一个类(字节码),将接口中的所有方法合并为一个
    2. 每个方法都使用相同的字节码构建,如

      1. 调用方法方法(生成时存储)
      2. 将信息传递到invocation handler的{​​{1}}
      3. invoke()的{​​{1}}
      4. 获取返回值
      5. 回来吧
    3. 类(字节码)以invocation handler

    4. 的形式表示

      如何绘制课程

      认为你的java代码被编译成字节码,只需在运行时

      执行此操作

      谈话很便宜显示代码

      sun / misc / ProxyGenerator.java中的核心方法

      generateClassFile

      invoke()

      addProxyMethod

      byte[]

      关于gen代理方法的完整代码

      /**
       * Generate a class file for the proxy class.  This method drives the
       * class file generation process.
       */
      private byte[] generateClassFile() {
      
          /* ============================================================
           * Step 1: Assemble ProxyMethod objects for all methods to
           * generate proxy dispatching code for.
           */
      
          /*
           * Record that proxy methods are needed for the hashCode, equals,
           * and toString methods of java.lang.Object.  This is done before
           * the methods from the proxy interfaces so that the methods from
           * java.lang.Object take precedence over duplicate methods in the
           * proxy interfaces.
           */
          addProxyMethod(hashCodeMethod, Object.class);
          addProxyMethod(equalsMethod, Object.class);
          addProxyMethod(toStringMethod, Object.class);
      
          /*
           * Now record all of the methods from the proxy interfaces, giving
           * earlier interfaces precedence over later ones with duplicate
           * methods.
           */
          for (int i = 0; i < interfaces.length; i++) {
              Method[] methods = interfaces[i].getMethods();
              for (int j = 0; j < methods.length; j++) {
                  addProxyMethod(methods[j], interfaces[i]);
              }
          }
      
          /*
           * For each set of proxy methods with the same signature,
           * verify that the methods' return types are compatible.
           */
          for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
              checkReturnTypes(sigmethods);
          }
      
          /* ============================================================
           * Step 2: Assemble FieldInfo and MethodInfo structs for all of
           * fields and methods in the class we are generating.
           */
          try {
              methods.add(generateConstructor());
      
              for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
                  for (ProxyMethod pm : sigmethods) {
      
                      // add static field for method's Method object
                      fields.add(new FieldInfo(pm.methodFieldName,
                          "Ljava/lang/reflect/Method;",
                           ACC_PRIVATE | ACC_STATIC));
      
                      // generate code for proxy method and add it
                      methods.add(pm.generateMethod());
                  }
              }
      
              methods.add(generateStaticInitializer());
      
          } catch (IOException e) {
              throw new InternalError("unexpected I/O Exception");
          }
      
          if (methods.size() > 65535) {
              throw new IllegalArgumentException("method limit exceeded");
          }
          if (fields.size() > 65535) {
              throw new IllegalArgumentException("field limit exceeded");
          }
      
          /* ============================================================
           * Step 3: Write the final class file.
           */
      
          /*
           * Make sure that constant pool indexes are reserved for the
           * following items before starting to write the final class file.
           */
          cp.getClass(dotToSlash(className));
          cp.getClass(superclassName);
          for (int i = 0; i < interfaces.length; i++) {
              cp.getClass(dotToSlash(interfaces[i].getName()));
          }
      
          /*
           * Disallow new constant pool additions beyond this point, since
           * we are about to write the final constant pool table.
           */
          cp.setReadOnly();
      
          ByteArrayOutputStream bout = new ByteArrayOutputStream();
          DataOutputStream dout = new DataOutputStream(bout);
      
          try {
              /*
               * Write all the items of the "ClassFile" structure.
               * See JVMS section 4.1.
               */
                                          // u4 magic;
              dout.writeInt(0xCAFEBABE);
                                          // u2 minor_version;
              dout.writeShort(CLASSFILE_MINOR_VERSION);
                                          // u2 major_version;
              dout.writeShort(CLASSFILE_MAJOR_VERSION);
      
              cp.write(dout);             // (write constant pool)
      
                                          // u2 access_flags;
              dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
                                          // u2 this_class;
              dout.writeShort(cp.getClass(dotToSlash(className)));
                                          // u2 super_class;
              dout.writeShort(cp.getClass(superclassName));
      
                                          // u2 interfaces_count;
              dout.writeShort(interfaces.length);
                                          // u2 interfaces[interfaces_count];
              for (int i = 0; i < interfaces.length; i++) {
                  dout.writeShort(cp.getClass(
                      dotToSlash(interfaces[i].getName())));
              }
      
                                          // u2 fields_count;
              dout.writeShort(fields.size());
                                          // field_info fields[fields_count];
              for (FieldInfo f : fields) {
                  f.write(dout);
              }
      
                                          // u2 methods_count;
              dout.writeShort(methods.size());
                                          // method_info methods[methods_count];
              for (MethodInfo m : methods) {
                  m.write(dout);
              }
      
                                           // u2 attributes_count;
              dout.writeShort(0); // (no ClassFile attributes for proxy classes)
      
          } catch (IOException e) {
              throw new InternalError("unexpected I/O Exception");
          }
      
          return bout.toByteArray();
      }
      

答案 1 :(得分:36)

  1. 代理是在运行时创建和加载的类。这些类没有源代码。我知道你想知道如果没有代码可以让他们做点什么。答案是,在创建它们时,指定一个实现InvocationHandler的对象,该对象定义了在调用代理方法时调用的方法。

  2. 您可以使用调用

    创建它们
    Proxy.newProxyInstance(classLoader, interfaces, invocationHandler)
    

    参数是:

    1. classLoader。生成类后,将使用此类加载器加载它。
    2. interfaces。必须全部是接口的类对象数组。生成的代理实现了所有这些接口。
    3. invocationHandler。这是您的代理在调用方法时知道该怎么做的方式。它是一个实现InvocationHandler的对象。当调用任何受支持的接口或hashCodeequalstoString的方法时,将在处理程序上调用方法invoke,并传递{{ 1}}要调用的方法和传递的参数的对象。
    4. 有关详情,请参阅Proxy课程的文档。

    5. 版本1.3之后的每个JVM实现都必须支持这些。它们以特定于实现的方式加载到JVM的内部数据结构中,但保证可以正常工作。