无法使用修饰符“public”访问类java.nio.DirectByteBuffer(在模块java.base中)的成员

时间:2017-12-19 16:33:51

标签: java reflection direct-buffer

获得了一个仍然支持Java 6的项目。下面的代码位于使用Compiler compliance level 1.6构建的jar文件中

应该从为java 6或更新版本构建的java应用程序中调用该jar文件。 它在Java 8中运行良好。

现在使用Java9,我遇到了nio.DirectByteBuffer的问题,我尝试用这种方式解决它,使用反射:

@SuppressWarnings("unchecked")
static void cleanDirectBuffer(sun.nio.ch.DirectBuffer buffer) {
    if (JAVA_VERSION < 1.9) {
        sun.misc.Cleaner cleaner = buffer.cleaner();
        if (cleaner != null) cleaner.clean();
    } else {
        // For java9 do it the reflection way
        @SuppressWarnings("rawtypes")
        Class B = buffer.getClass(); 
        // will be a java.nio.DirectBuffer, which is unknown if compiled in 1.6 compliance mode
        try {
            java.lang.reflect.Method CleanerMethod = B.getMethod("cleaner");
            CleanerMethod.setAccessible(true);  // fails here !
            Object cleaner = CleanerMethod.invoke(buffer);
            if (cleaner == null) return;
            @SuppressWarnings("rawtypes")
            Class C = cleaner.getClass();
            java.lang.reflect.Method CleanMethod = C.getMethod("clean");
            CleanMethod.invoke(cleaner);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (Exception other) {
            other.printStackTrace();
        }
    }
}

JAVA_VERSION检测很好并且可以很好地切换,具体取决于我的调用代码使用的版本。 jre6到jre8环境很好地使用sun.misc.Cleaner路径,但这在java9中不起作用

您可能会注意到我不是java.reflection的专家。 通过猜测,我找到了.setAccessible(true);

和lance-java的回答(感谢到目前为止)有点帮助:

版本2:

    Class B = buffer.getClass(); 
    try {
        java.lang.reflect.Method CleanerMethod = B.getDeclaredMethod("cleaner");
        CleanerMethod.setAccessible(true);
        Object cleaner = CleanerMethod.invoke(buffer);
        if (cleaner == null) return;
        @SuppressWarnings("rawtypes")
        Class C = cleaner.getClass();
        java.lang.reflect.Method CleanMethod = C.getDeclaredMethod("clean");
        CleanMethod.setAccessible(true); // Now it fails here !
        CleanMethod.invoke(cleaner);
    } catch (InaccessibleObjectException e) {
         // ** causes: Unable to make public void jdk.internal.ref.Cleaner.clean() accessible: module java.base does not "exports jdk.internal.ref" to unnamed module  **
    }

此外警告第一个CleanerMethod.setAccessible(true)

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by my.package.MyClass  (file:/xyz.jar) to method java.nio.DirectByteBuffer.cleaner()
...
WARNING: All illegal access operations will be denied in a future release

......听起来不太健康?但唉,这只是一个警告:)

我还缺少什么,或者我的问题有不同/更好的方法?

3 个答案:

答案 0 :(得分:6)

在Java 9中引入了模块概念(https://blog.codefx.org/java/java-module-system-tutorial/)。

  

模块列出它导出的包。对于一个模块中的代码(比如org.codefx.demo.jpms)来访问另一个模块中的代码(比如java.base中的String),必须满足以下可访问性规则:

     

访问类型(String)必须是公共的

     

包含类型(java.lang)的包必须由其模块(java.base)

导出      

访问模块(org.codefx.demo.jpms)必须读取访问过的模块(java.base),这通常是通过要求它来实现的

     

如果在编译或运行时违反了这些规则中的任何一个,则模块系统会抛出错误。这意味着公众不再是公开的。非导出包中的公共类型与导出包中的非公共类型一样,外部世界无法访问。还要注意反射失去了超级大国。

现在可以定义可在包外部访问的类/方法。即使它们是公共的,其他模块也无法访问所有其他非指定的类/方法。这是指向module-info http://people.redhat.com/mbalaoal/webrevs/jdk_8029661_tls_12_sunpkcs11/2018_02_02/8029661.webrev.02/src/java.base/share/classes/module-info.java.html java.base的链接。如您所见jdk.internal.ref仅导出到这些模块:java.desktopjdk.unsupported

答案 1 :(得分:2)

sun.nio.ch.DirectBufferjava.nio.DirectByteBuffer是内部JDK的一部分,因此您不应该依赖这些类。它们随时都会消失。

如果您希望清除它们,请不要引用DirectByteBuffer

答案 2 :(得分:0)

Class.getMethod()Class.getField()等只会返回公共方法/字段。我想你想要Class.getDeclaredMethod()