当前位置: 首页 > 知识库问答 >
问题:

如何在没有JVM参数的情况下隐藏java 9中的警告“非法反射访问”?

陶俊晤
2023-03-14

我只是尝试使用Java 9运行我的服务器,并得到下一个警告:

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by io.netty.util.internal.ReflectionUtil (file:/home/azureuser/server-0.28.0-SNAPSHOT.jar) to constructor java.nio.DirectByteBuffer(long,int)
WARNING: Please consider reporting this to the maintainers of io.netty.util.internal.ReflectionUtil
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

我希望隐藏此警告,而不在启动期间向JVM选项添加<code>--非法访问=拒绝</code>。类似于:

System.setProperty("illegal-access", "deny");

有什么办法吗?

所有建议使用JVM选项的相关答案,我想从代码中关闭它。这可能吗?

为了澄清-我的问题是关于从代码中转换此警告,而不是通过类似问题中所述的JVM参数/标志。

共有3个答案

曾丰茂
2023-03-14
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Main {
    @SuppressWarnings("unchecked")
    public static void disableAccessWarnings() {
        try {
            Class unsafeClass = Class.forName("sun.misc.Unsafe");
            Field field = unsafeClass.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            Object unsafe = field.get(null);

            Method putObjectVolatile = unsafeClass.getDeclaredMethod("putObjectVolatile", Object.class, long.class, Object.class);
            Method staticFieldOffset = unsafeClass.getDeclaredMethod("staticFieldOffset", Field.class);

            Class loggerClass = Class.forName("jdk.internal.module.IllegalAccessLogger");
            Field loggerField = loggerClass.getDeclaredField("logger");
            Long offset = (Long) staticFieldOffset.invoke(unsafe, loggerField);
            putObjectVolatile.invoke(unsafe, loggerClass, offset, null);
        } catch (Exception ignored) {
        }
    }

    public static void main(String[] args) {
        disableAccessWarnings();
    }
}

它在JAVA 11中对我有效。

柏夕
2023-03-14

还有另一个选项,它不需要流抑制,也不依赖于未记录或不支持的API。使用Java代理,可以重新定义模块以导出/打开所需的包。代码如下所示:

void exportAndOpen(Instrumentation instrumentation) {
  Set<Module> unnamed = 
    Collections.singleton(ClassLoader.getSystemClassLoader().getUnnamedModule());
  ModuleLayer.boot().modules().forEach(module -> instrumentation.redefineModule(
        module,
        unnamed,
        module.getPackages().stream().collect(Collectors.toMap(
          Function.identity(),
          pkg -> unnamed
        )),
        module.getPackages().stream().collect(Collectors.toMap(
           Function.identity(),
           pkg -> unnamed
         )),
         Collections.emptySet(),
         Collections.emptyMap()
  ));
}

现在,您可以运行任何非法访问而不会出现警告,因为您的应用程序包含在未命名的模块中,例如:

Method method = ClassLoader.class.getDeclaredMethod("defineClass", 
    byte[].class, int.class, int.class);
method.setAccessible(true);

为了获得Instrumence实例,您可以编写一个非常简单的Java代理,并使用-javaagent:myjar.jar在命令行(而不是类路径)上指定它。代理将只包含一个premain方法,如下所示:

public class MyAgent {
  public static void main(String arg, Instrumentation inst) {
    exportAndOpen(inst);
  }
}

或者,您可以使用byte-party-agent项目(我编写的)方便访问的附加API动态附加:

exportAndOpen(ByteBuddyAgent.install());

您需要在非法访问之前调用它。请注意,这仅在 JDK 和 Linux VM 上可用,而如果您在其他 VM 上需要,则需要在命令行上提供 Byte Buddy 代理作为 Java 代理。当您希望在通常安装了 JDK 的测试和开发计算机上进行自连接时,这可能很方便。

正如其他人所指出的,这应该只是一个中间解决方案,但我完全理解当前的行为经常会破坏日志爬虫和控制台应用程序,这就是为什么我自己在生产环境中使用它作为使用Java9的短期解决方案,这么长时间我没有遇到任何问题。

然而,好的一面是,这种解决方案对未来的任何操作更新都是健壮的,即使动态附件是合法的。使用一个助手进程,Byte Buddy甚至可以绕过通常被禁止的自连接。

冀越
2023-03-14

有一些方法可以禁用非法访问警告,但我不建议这样做。

由于警告打印到默认错误流,因此您可以简单地关闭此流并将stderr重定向到stdout

public static void disableWarning() {
    System.err.close();
    System.setErr(System.out);
}

注意事项:

    < li >这种方法合并了错误和输出流。在某些情况下,这可能是不可取的。 < li >您不能仅通过调用< code>System.setErr来重定向警告消息,因为对错误流的引用在JVM引导早期保存在< code > illegalaccesslogger . warning stream 字段中。

一个好消息是,在JDK 9中仍然可以在没有警告的情况下访问< code>sun.misc.Unsafe。解决方法是借助不安全API重置内部< code>IllegalAccessLogger。

public static void disableWarning() {
    try {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        Unsafe u = (Unsafe) theUnsafe.get(null);

        Class cls = Class.forName("jdk.internal.module.IllegalAccessLogger");
        Field logger = cls.getDeclaredField("logger");
        u.putObjectVolatile(cls, u.staticFieldOffset(logger), null);
    } catch (Exception e) {
        // ignore
    }
}
 类似资料:
  • 问题内容: 我只是尝试使用Java 9运行服务器,并收到下一个警告: 我想隐藏此警告而不在启动过程中添加到JVM选项。就像是: 有什么办法吗? 建议使用JVM选项的所有相关答案,我想从代码中关闭它。那可能吗? 要澄清-我的问题是关于从代码而不是通过类似问题中所述的JVM参数/标志来启用此警告。 问题答案: 有几种方法可以禁用非法访问警告,尽管我不建议您这样做。 1.简单的方法 由于警告已打印到默认

  • 我正在遵循Oracle的教程在JavaFX中创建TableView。在这个截图中,我复制并粘贴了代码,发现了几个错误,教程中没有提到这些错误。 TableView上的警告显示: TableView是原始类型。对泛型类型TableView的引用 表列上的警告说: 表列是一个原始类型。对泛型类型TableCol列的引用 addAll方法上的警告是: 类型安全:方法addAll(Object…)属于原始

  • 如何为非法反射访问警告引发异常?例如,考虑以下代码: 此代码将以下警告输出到 System.err: Boolean.TRUE是一个如此简单的值,以至于我真的不需要RefltionToStringBuilder。但是更复杂的类型(例如HashMap)打印相同的警告。我选择Boolean.TRUE是为了简化这个例子。 当我搜索此警告消息时,我发现了将其报告给包维护者,避免警告或完全禁用它的建议(JD

  • 如果能帮助我重写一些使用反射的Java代码,以便在Java10上删除编译器中的警告,我将不胜感激: 这就是所讨论的Java方法: 这是GitHub上的问题代码:https://github.com/librepdf/openpdf/blob/master/openpdf/src/main/java/com/lowagie/text/pdf/mappedrandomaccessfile.java#l

  • 我正在尝试设置一个动态,从本地目录读取文件。因此,我创建了以下可参数化类: 参数化流在的帮助下注册,如下所示: 启动应用程序后,我得到以下警告: 如果我理解正确的话,负责创建集成bean(从而设置beanFactory)。但是为什么我会收到这个错误信息呢?

  • 我在Eclipse(版本2019-06)中运行一个项目,它在我的java计算机上运行得很好--“OpenJDK 1.8.0_222” 然而,当我尝试tor在Eclipse(版本2021-3)上的另一台计算机上用java运行该项目时--“OpenJDK版本”1.8.0_292“,它不起作用! 我得到以下错误: 警告:发生非法反射访问操作警告:com.google.gson.internal.bind