【问题标题】:java dynamic class loading that avoids java.lang.IllegalAccessError避免 java.lang.IllegalAccessError 的 java 动态类加载
【发布时间】:2017-06-23 15:49:15
【问题描述】:

Oracle JavaDocs 解释说 IllegalAccessError 是

"如果应用程序尝试访问或修改字段,或者要 调用它无权访问的方法。”

我尝试动态加载一个类,但我得到了这个异常。

如果我理解正确,当您使用类加载器动态加载带有私有包的类时会发生 IllegalAccessError

我要加载的类正在使用

org.xml.sax.helpers.SecuritySupport

这也在以下网址中的描述中说明 http://grepcode.com/file/repository.springsource.com/org.apache.xmlcommons/com.springsource.org.apache.xmlcommons/1.3.4/org/xml/sax/helpers/SecuritySupport.java 那个

很遗憾,我们无法使用反射加载类 * 因为该类是包私有的。而且班级有 * 包私有,因此 API 不会暴露给其他人 * 可以使用它们来规避安全性的代码。因此, * 我们接受直接引用可能失败的风险 * 在一些 JDK 1.1 JVM 上,即使我们永远不会执行 * 这种情况下的代码。叹息……

我怎样才能动态加载它呢?我必须让它工作。 另外,如果我在使用类加载器时遇到错误,我无法从中恢复,那么我怎么能提前知道我无法加载这个类呢?

提前感谢任何提供帮助的人

【问题讨论】:

    标签: java classloader dynamic-loading


    【解决方案1】:

    我们不能使用反射加载类,因为类是包私有的”这句话没有任何意义,可以很容易地证明:

    package somepackage;
    
    class BaseClass {
        public static void main(String[] args) throws ReflectiveOperationException {
            BaseClass obj=(BaseClass)
                Class.forName("somepackage.SubClass").newInstance();
            obj.aMethod();
        }
    
        void aMethod() {
            System.out.println("base class");
        }
    }
    class SubClass extends BaseClass {
        @Override
        void aMethod() {
            System.out.println("method overridden by subclass");
        }
    }
    

    这完美无缺,打印 method overridden by subclass 复制了 SecuritySupport 类的实际用例。

    但是,由于该类显然是为了允许在 Java 1.1 和 Java 1.2 之间进行转换,因此在 20 年前发生这种转换时,可能存在这样的限制。

    但是,您的用例完全不同。您说您正在尝试加载一个“正在使用org.xml.sax.helpers.SecuritySupport”的类,这并不意味着它正在通过反射使用该类,但如上所示,这并不重要。无论哪种情况,只有当类在同一个包中时,它才会起作用,无论您是否“动态”加载该类。

    只有两种可能的情况。

    1. 如果该类确实在同一个包中,这在运行时意味着它也已由同一个类加载器加载,这也要求它也是 JRE 的一部分,如果 JRE 的 org.xml.sax.helpers 包定义了一个 @ 987654326@ 类,则该类可以访问同一个包内的类。

    2. 如果您尝试从不同的代码源通过不同的ClassLoader 加载一个类,即使您给它一个org.xml.sax.helpers.SomeClass 形式的限定名称,它也不会属于该包。如果 JRE 的 org.xml.sax.helpers 包恰好定义了一个 SecuritySupport 类,则所有非 JRE 类将位于不同的包中。当它尝试访问不属于官方 API 的该类时,它不起作用。

      请注意,所有标准类加载器都遵循委托模型,试图首先通过其父类加载器解析名称,这就是为什么他们都更喜欢 JRE 的 org.xml.sax.helpers.SecuritySupport 类(如果有的话)的原因。使用非标准类加载器,您可以拥有具有该限定名称的不同、不相关的类,它们位于不同的运行时包中。

    在第二种情况下,问题出现了,为什么您的班级使用该班级。在 2017 年,几乎不需要区分 Java 1.1 和 Java 1.2,并且该类提供的功能也仅与 JRE 的特权代码源(或具有一般不同的特权)。

    【讨论】:

    • 由于您上面的示例类没有定义构造函数,它创建了一个默认构造函数,(我认为)默认情况下是公共的。因此,当您致电 newInstance 时,没有 IAE。对于SecuritySupport,情况可能并非如此。您可以尝试添加一个包私有构造函数,看看您是否仍然可以创建一个新实例?我猜它不会起作用。
    • 实际上,您的主要方法(在​​其中创建类的新实例)与示例类位于同一个包中,因此您的示例当然可以工作。如果您的主类与您动态创建的类位于不同的包中,您能否检查它是否有效?
    • @Alvin Thompson:SecuritySupportSecuritySupport12 在同一个包中,这就是重点。所以不清楚为什么SecuritySupport 包含无法通过反射加载SecuritySupport12 的声明。不要忘记,声明的结果是SecuritySupport 直接加载和实例化SecuritySupport12,没有反射,当然,反射也依赖于同一个包。
    • @Alvin Thompson:顺便说一下,SecuritySupportSecuritySupport12 都没有显式构造函数,这并不重要,因为编译器生成的默认构造函数具有与类相同的访问级别,所以这些非public 类也有非public 构造函数。但具有讽刺意味的是,这个包私有类在最近的 JRE 中甚至都不存在,所以当只使用官方的org.xml.sax.helpers API 时,你永远不会遇到它,即使在幕后也不会。
    • 是的,这是个谜。但是,让我们回到 OP 的问题。当尝试动态加载他的类时,类加载器必须首先尝试动态加载SecuritySupport,因为他的类需要它。
    猜你喜欢
    • 2018-10-05
    • 2011-04-04
    • 2012-10-10
    • 1970-01-01
    • 1970-01-01
    • 2012-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多