簡體   English   中英

什么是非法反射訪問?

[英]What is an illegal reflective access?

Java 9 中有很多關於非法反射訪問的問題。

我發現了很多關於解決錯誤消息的討論,但我很想知道非法反射訪問實際上是什么。

所以我的問題是:

什么定義了非法反射訪問以及什么情況會觸發警告?

我收集到它與 Java 9 中引入的封裝原則有關,但我找不到關於它們如何結合在一起、觸發警告的原因以及在什么情況下的解釋。

除了了解模塊及其各自包之間的訪問之外。 我相信它的症結在於模塊系統#Relaxed-strong-encapsulation ,我只想挑選它的相關部分來嘗試回答這個問題。

什么定義了非法反射訪問以及什么情況會觸發警告?

為了幫助遷移到 Java-9,可以放松模塊的強封裝。

  • 一個實現可以提供靜態訪問,即通過編譯的字節碼。

  • 可以提供一種方法來調用其運行時系統,其中一個或多個模塊的一個或多個包對所有未命名模塊中的代碼開放,即在類路徑上進行編碼。 如果以這種方式調用運行時系統,並且如果這樣做,則反射 API 的某些調用會成功,否則它們將失敗。

在這種情況下,您實際上最終進行了“非法”反射訪問,因為在純模塊化世界中,您並不打算進行此類訪問。

它是如何結合在一起的,什么情況下會觸發警告?

這種封裝的放松在運行時由一個新的啟動器選項--illegal-access ,它在 Java9 中默認等於permit permit模式確保

對任何此類包的第一次反射訪問操作會導致發出警告,但在那之后不會發出任何警告。 此單個警告描述了如何啟用更多警告。 無法抑制此警告。

這些模式可以使用值debug (每個此類訪問的消息以及堆棧跟蹤)、 warn (每個此類訪問的消息)和deny (禁用此類操作)進行配置。


調試和修復應用程序的幾件事是:-

  • 使用--illegal-access=deny運行它以了解並避免在沒有模塊聲明的情況下從一個模塊打開包到另一個模塊( opens )或顯式使用--add-opens VM arg。
  • 可以使用帶有--jdk-internals選項的jdeps工具識別從編譯代碼到 JDK 內部 API 的靜態引用

檢測到非法反射訪問操作時發出的警告消息具有以下形式:

 WARNING: Illegal reflective access by $PERPETRATOR to $VICTIM

在哪里:

$PERPETRATOR是包含調用相關反射操作的代碼以及代碼源(即 JAR 文件路徑)(如果可用)的類型的完全限定名稱,以及

$VICTIM是描述被訪問成員的字符串,包括封閉類型的完全限定名稱

此類示例警告的問題: = JDK9:發生了非法反射訪問操作。 org.python.core.PySystemState

最后一個重要的注意事項,在努力確保您不會面臨此類警告並且將來是安全的時,您需要做的就是確保您的模塊不會進行那些非法的反射訪問。 :)

我發現了一篇關於 Java 9 模塊系統的 Oracle 文章

默認情況下,模塊中的類型不能被其他模塊訪問,除非它是公共類型並且您導出它的包。 您只公開要公開的包。 對於 Java 9,這也適用於反射。

正如https://stackoverflow.com/a/50251958/134894 中所指出的,JDK8 和 JDK9 的AccessibleObject#setAccessible之間的差異具有指導意義。 具體來說,JDK9添加了

如果以下任何一項成立,則類 C 中的調用者可以使用此方法來啟用對聲明類 D 的成員的訪問:

  • C 和 D 在同一個模塊中。
  • 在包含 D 的模塊導出到至少包含 C 的模塊的包中,成員是公共的,D 是公共的。
  • 該成員是 protected static,D 在包含 D 的模塊導出到至少包含 C 的模塊的包中是公共的,並且 C 是 D 的子類。
  • D 位於包含 D 的模塊至少對包含 C 的模塊開放的包中。未命名和開放模塊中的所有包對所有模塊開放,因此當 D 位於未命名或開放模塊中時,此方法總是成功。

它突出了模塊及其導出的重要性(在 Java 9 中)

只需查看用於訪問private字段和方法的setAccessible()方法:

https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible-boolean-

https://docs.oracle.com/javase/9​​/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible-boolean-

現在,此方法需要更多條件才能起作用。 它不會破壞幾乎所有舊軟件的唯一原因是從普通 JAR 自動生成的模塊非常寬松(為每個人打開並導出所有內容)。

如果你想使用 add-open 選項,這里有一個命令來查找哪個模塊提供了哪個包 ->

java --list-modules | tr @ " " | awk '{ print $1 }' | xargs -n1 java -d

模塊的名稱將與@一起顯示,而沒有它的包的名稱

注意:使用 JDK 11 測試

重要提示:顯然比包的提供者不做非法訪問要好

如果你想隱藏警告,你可以使用“--add-opens”選項

--add-opens <source-module>/<package>=<target-module>(,<target-module>)*

例如你有一個錯誤:

java.lang.ClassLoader.findLoadedClass(java.lang.String)

首先打開 String java 11 文檔Class String在這里你可以找到模塊和包名

模塊 java.base,包 java.lang

解決方案:

java --add-opens=java.base/java.lang=ALL-UNNAMED -jar example.jar

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM