Dmi*_*kiy 50 java jvm netty java-9
我只是尝试用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
Run Code Online (Sandbox Code Playgroud)
我想隐藏此警告而不--illegal-access=deny在启动期间添加JVM选项.就像是:
System.setProperty("illegal-access", "deny");
Run Code Online (Sandbox Code Playgroud)
有没有办法做到这一点?
建议使用JVM选项的所有相关答案,我想从代码中关闭它.那可能吗?
澄清 - 我的问题是关于从代码中转发此警告而不是通过类似问题中所述的JVM参数/标志.
apa*_*gin 46
有一些方法可以禁用非法访问警告,但我不建议这样做.
由于警告打印到默认错误流,您只需关闭此流并重定向stderr到stdout.
public static void disableWarning() {
System.err.close();
System.setErr(System.out);
}
Run Code Online (Sandbox Code Playgroud)
笔记:
System.setErr,因为错误流的引用IllegalAccessLogger.warningStream在JVM引导程序的早期保存在字段中.一个好消息是sun.misc.Unsafe仍然可以在没有警告的情况下在JDK 9中访问.解决方案是在IllegalAccessLoggerUnsafe API的帮助下重置内部.
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
}
}
Run Code Online (Sandbox Code Playgroud)
Raf*_*ter 13
还有另一个选项不需要流抑制,也不依赖于未记录或不支持的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()
));
}
Run Code Online (Sandbox Code Playgroud)
您现在可以在没有警告的情况下运行任何非法访问,因为您的应用程序包含在未命名的模块中,例如:
Method method = ClassLoader.class.getDeclaredMethod("defineClass",
byte[].class, int.class, int.class);
method.setAccessible(true);
Run Code Online (Sandbox Code Playgroud)
为了掌握Instrumentation实例,你可以编写一个非常简单的Java代理,并在命令行(而不是类路径)上使用它来指定它-javaagent:myjar.jar.代理只包含premain如下方法:
public class MyAgent {
public static void main(String arg, Instrumentation inst) {
exportAndOpen(inst);
}
}
Run Code Online (Sandbox Code Playgroud)
或者,你可以动态地连接使用由制造方便访问的API附加的字节好友代理的项目(这是我创作的):
exportAndOpen(ByteBuddyAgent.install());
Run Code Online (Sandbox Code Playgroud)
在非法访问之前您需要调用它.请注意,这仅适用于JDK和Linux VM,而如果您需要在其他VM上使用,则需要在命令行上提供Byte Buddy代理作为Java代理.当您希望在通常安装JDK的测试和开发机器上进行自我附件时,这很方便.
正如其他人所指出的那样,这应该只是一个中间解决方案,但我完全理解当前的行为通常会破坏日志记录爬虫和控制台应用程序,这就是为什么我自己在生产环境中使用它作为使用Java 9的短期解决方案和这么久我没有遇到任何问题.
然而,好处是这个解决方案对于未来的更新是强大的,因为任何操作,甚至动态附件都是合法的.使用帮助程序,Byte Buddy甚至可以解决通常禁止的自我附着问题.
还有另一种方法,不基于任何黑客,在上面的任何答案中都没有提到。然而,它仅适用于在类路径上运行的代码。所以任何需要支持在 Java 9+ 上运行的库都可以使用这种技术,只要它是从类路径运行的。
它基于这样一个事实,即允许在类路径上运行的代码(即来自未命名模块)自由动态地打开任何模块的包(它只能从目标模块本身或未命名模块完成)。
例如,给定此代码,访问java.io.Console类的私有字段:
Field field = Console.class.getDeclaredField("formatter");
field.setAccessible(true);
Run Code Online (Sandbox Code Playgroud)
为了不引起警告,我们必须打开目标模块的包到我们的模块:
if (!ThisClass.class.getModule().isNamed()) {
Console.class.getModule().addOpens(Console.class.getPackageName(), ThisClass.class.getModule());
}
Run Code Online (Sandbox Code Playgroud)
我们还添加了一个检查,我们确实在类路径上运行。
小智 5
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();
}
}
Run Code Online (Sandbox Code Playgroud)
它在JAVA 11中对我有用。
小智 5
我想出了一种方法来禁用该警告,而无需使用 Unsafe 或访问任何未记录的 API。它的工作原理是使用反射将FilterOutputStream::out的字段设置System.err为空。
当然,尝试使用反射实际上会抛出我们试图抑制的警告,但我们可以利用并发来解决这个问题:
System.err以便其他线程无法写入它。setAccessible产生 2 个在场上调用的线程out。其中一个在尝试显示警告时将挂起,但另一个将完成。out将的字段设置System.err为 null 并释放 的锁定System.err。第二个线程现在将完成,但不会显示任何警告。out的字段System.err。下面的代码演示了这一点:
public void suppressWarning() throws Exception
{
Field f = FilterOutputStream.class.getDeclaredField("out");
Runnable r = () -> { f.setAccessible(true); synchronized(this) { this.notify(); }};
Object errorOutput;
synchronized (this)
{
synchronized (System.err) //lock System.err to delay the warning
{
new Thread(r).start(); //One of these 2 threads will
new Thread(r).start(); //hang, the other will succeed.
this.wait(); //Wait 1st thread to end.
errorOutput = f.get(System.err); //Field is now accessible, set
f.set(System.err, null); // it to null to suppress the warning
} //release System.err to allow 2nd thread to complete.
this.wait(); //Wait 2nd thread to end.
f.set(System.err, errorOutput); //Restore System.err
}
}
Run Code Online (Sandbox Code Playgroud)
即使--illegal-access设置为“警告”或“调试”,此代码也将起作用,因为这些模式不会为同一调用者多次显示警告。
System.err此外,您还可以将其字段设置为自定义 OutputStream,而不是恢复 的原始状态out,以便您可以过滤未来的警告。
| 归档时间: |
|
| 查看次数: |
49107 次 |
| 最近记录: |