web-dev-qa-db-ja.com

JVM引数なしの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

起動時に--illegal-access=denyをJVMオプションに追加せずにこの警告を非表示にしたいと思います。何かのようなもの:

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

それを行う方法はありますか?

JVMオプションを使用することを示唆するすべての関連する答えは、コードからこれをオフにしたいと思います。それは可能ですか?

明確にするために-私の質問は、この警告をコードから変えることに関するものであり、同様の質問で述べられているJVM引数/フラグを介してではありません。

48

不正アクセスの警告を無効にする方法がありますが、これはお勧めしません。

1.シンプルなアプローチ

警告はデフォルトのエラーストリームに出力されるため、このストリームを閉じてstderrstdoutにリダイレクトするだけです。

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

ノート:

  • このアプローチは、エラーストリームと出力ストリームをマージします。場合によっては望ましくない場合があります。
  • JVMブートストラップの早い段階でエラーストリームへの参照がSystem.setErrフィールドに保存されるため、IllegalAccessLogger.warningStreamを呼び出すだけでは警告メッセージをリダイレクトできません。

2. stderrを変更しない複雑なアプローチ

良いニュースは、Sun.misc.Unsafeに警告なしでJDK 9で引き続きアクセスできることです。解決策は、Unsafe APIを使用して内部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
    }
}
45
apangin

ストリーム抑制の必要性がなく、ドキュメント化されていない、またはサポートされていない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);

Instrumentationインスタンスを取得するには、 Javaエージェント を記述して、-javaagent:myjar.jarを使用してコマンドライン(クラスパスではなく)で指定します。エージェントには、次のpremainメソッドのみが含まれます。

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

あるいは、 thebyte-buddy-agentproject (これにより便利にアクセスできるアタッチAPIを使用して動的にアタッチできます。私が作成した):

exportAndOpen(ByteBuddyAgent.install());

違法なアクセスの前に電話する必要があります。これは、JDKおよびLinux VMでのみ使用できますが、他のVMで必要な場合は、コマンドラインでByte BuddyエージェントをJavaエージェントとして提供する必要があることに注意してください。これは、通常JDKがインストールされているテストおよび開発マシンで自己接続が必要な場合に便利です。

他の人が指摘したように、これは中間ソリューションとしてのみ機能するはずですが、現在の動作がクローラーとコンソールアプリのロギングを壊すことがよくあることを完全に理解しています。これが、Java 9そして、私は問題に遭遇しなかった。

ただし、このソリューションは、動的な添付ファイルでも合法であるため、どのような操作でも将来の更新に対して堅牢です。ヘルパープロセスを使用して、Byte Buddyは通常禁止されている自己アタッチメントを回避します。

12

あなたが求めていることを達成する方法はありません。指摘したように、JVM起動に コマンドラインオプション (ただし、--add-opensではなく、--illegal-access=deny)を追加する必要があります。

あなたが書いた:

私の目標は、エンドユーザー向けの追加の指示を避けることです。サーバーをインストールしているユーザーが多数いるため、ユーザーにとっては大きな不便です。

見た目では、あなたの要件はプロジェクトがJavaの準備ができていないという結論を残しているだけです。9. Java 9互換。これは、リリース後の早い段階でまったく問題ありません。

10
Nicolai
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で機能します。

3
Gan

module-info.Javaopenパッケージを作成するか、open moduleを作成できます。

例: プロジェクトをジグソーパズルに段階的に移行する のステップ5と6をチェックアウトする

module shedlock.example {
    requires spring.context;
    requires spring.jdbc;
    requires slf4j.api;
    requires shedlock.core;
    requires shedlock.spring;
    requires HikariCP;
    requires shedlock.provider.jdbc.template;
    requires Java.sql;
    opens net.javacrumbs.shedlockexample to spring.core, spring.beans, spring.context;
}

open module shedlock.example {
    requires spring.context;
    requires spring.jdbc;
    requires slf4j.api;
    requires shedlock.core;
    requires shedlock.spring;
    requires HikariCP;
    requires shedlock.provider.jdbc.template;
    requires Java.sql;
}
1
TheKojuEffect