【问题标题】:Java: How to load Class stored as byte[] into the JVM?Java:如何将存储为 byte[] 的类加载到 JVM 中?
【发布时间】:2009-11-23 04:28:54
【问题描述】:

如果已经将整个 .class 文件序列化为 byte[],并且假设类的名称是已知的(与 byte[] 一起传递),那么如何转换 byte[] -> Class -> 然后加载它到 JVM 以便我以后可以通过调用 Class.forName() 来使用它?

注意: 我这样做是因为我将 .class 发送到另一个主机,而主机的 JVM 不知道这个 .class。

【问题讨论】:

    标签: java classloader


    【解决方案1】:

    我现在实际上在测试中使用这样的东西来将一组类定义作为 byte[] 提供给 ClassLoader:

      public static class ByteClassLoader extends URLClassLoader {
        private final Map<String, byte[]> extraClassDefs;
    
        public ByteClassLoader(URL[] urls, ClassLoader parent, Map<String, byte[]> extraClassDefs) {
          super(urls, parent);
          this.extraClassDefs = new HashMap<String, byte[]>(extraClassDefs);
        }
    
        @Override
        protected Class<?> findClass(final String name) throws ClassNotFoundException {
          byte[] classBytes = this.extraClassDefs.remove(name);
          if (classBytes != null) {
            return defineClass(name, classBytes, 0, classBytes.length); 
          }
          return super.findClass(name);
        }
    
      }
    

    【讨论】:

    • Alex,但是如果 2 .class 相互依赖怎么办?还能用吗?
    • 可能不会。您可能应该发送(例如)一个 JAR 文件,而不是一系列单独的 .class 文件。
    • @ShaChris23 - 当然,为什么不呢? @Stephen C - 这似乎是对 Java 类加载器可以做什么的有限看法。为什么说可能不是?
    【解决方案2】:

    使用ClassLoader中的defineClass(String, byte[], int, int) 方法

    【讨论】:

      【解决方案3】:

      扩展ClassLoader,然后在方法defineClass() 中执行必要的调用以获取您的byte[]。相反,您可以在findClass() 中执行此操作。因此,您将在开始时加载此 ClassLoader,或者在您需要通过数组定义类时使用它。

      public class ByteArrayClassLoader extends ClassLoader {
      
          public Class findClass(String name) {
              byte[] ba = /* go obtain your byte array by the name */;
      
              return defineClass(name,ba,0,ba.length);
          }
      
      }
      

      【讨论】:

        【解决方案4】:

        好的,这就是我所做的:

        public class AgentClassLoader extends ClassLoader
        {
          public void loadThisClass(ClassByte classByte_)
          {
            resolveClass(defineClass(classByte_.getName(),
                                     classByte_.getBytes(),
                                     0,
                                     classByte_.getBytes().length));
          }
        }
        

        我希望将类加载到 JVM 中?如果这真的有效,为什么 Java 实现者不同时创建 defineClass 和 resolveClass 公共方法?我真的很想知道他们在想什么。哪位大神可以赐教一下?

        为了完整起见,这里是 ClassByte

        import java.io.Serializable;
        
        public class ClassByte implements Serializable
        {
          private String name;
          private byte[] bytes;
        
          ClassByte(String name_, byte[] bytes_)
          {
            name  = name_;
            bytes = bytes_;
          }
        
          public String getName()
          {
            return name;
          }
        
          public byte[] getBytes()
          {
            return bytes;
          }
        }
        

        【讨论】:

        • 我认为这个想法是阻止随机代码在系统/默认类加载器上调用defineClass。这有可能危及安全性,并且通常会使非常奇怪的事情发生。最好只在自定义类加载器上允许这样做。
        【解决方案5】:

        Alex 的回答是正确的,但是如果你的类之间有继承关系,你需要确保先加载父类。否则类加载器将无法定义它。

        【讨论】:

        • 真的吗?我认为这是一个链接问题而不是类定义问题。类不需要以正确的顺序定义,否则我们不能在类中有循环依赖。例如,Class.getName() 返回一个 String,但 String.getClass() 返回一个 Class。此外,这两个类都有 Object 的父类,但 Object.toString() 和 Object.getClass() 分别返回 String 和 Class。如果你说的是真的,那么加载这些类就没有正确的顺序。
        • @Kidburla 那是因为JVM会在需要的时候为类加载,所以你可以在不需要加载String的情况下加载Class,当你调用getName时会加载String。但是,超类/接口需要在加载类的同时或之前加载,否则类是不完整的。这就是为什么我们不能进行循环继承,因为类加载器不知道如何处理。
        • 是的。抱歉,由于接受答案的 cmets,我将“继承”误读为“依赖”。
        • @ıɯɐƃoʇǝızuǝʞ 不能进行循环继承的原因是因为它在逻辑上是不可能的,而不是因为加载它会有困难。并且超类/接口不需要在类加载之前加载,因为这也会导致逻辑上的不可能性:除非你先加载了类,否则你不知道 super 是什么,所以你不能加载超级课前。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-08-01
        • 2021-05-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-16
        相关资源
        最近更新 更多