¿Qué es un acceso reflectante ilegal?

126

Hay muchas preguntas sobre el acceso reflectante ilegal en Java 9.

Ahora, lo que no puedo encontrar porque todo lo que Google arroja son personas que intentan evitar los mensajes de error, es lo que realmente es un acceso reflectante ilegal.

Entonces mi pregunta bastante simple es:

¿Qué define un acceso reflectante ilegal y qué circunstancias desencadenan la advertencia?

He deducido que tiene algo que ver con los principios de encapsulación que se introdujeron en Java 9, pero cómo todo encaja y qué desencadena la advertencia en qué escenario no puedo encontrar una explicación.

Tschallacka
fuente
2
esto también puede interesarle: jaxenter.com/jdk-9-replace-permit-illegal-access-134180.html
Edwin

Respuestas:

54

Además de una comprensión de los accesos entre módulos y sus respectivos paquetes. Creo que el quid de esto radica en el Sistema de módulos # Relajado-fuerte-encapsulado y simplemente seleccionaría las partes relevantes para tratar de responder la pregunta.

¿Qué define un acceso reflectante ilegal y qué circunstancias desencadenan la advertencia?

Para ayudar en la migración a Java-9, se podría relajar la encapsulación sólida de los módulos.

  • Una implementación puede proporcionar acceso estático , es decir, mediante código de bytes compilado.

  • Puede proporcionar un medio para invocar su sistema de tiempo de ejecución con uno o más paquetes de uno o más de sus módulos abiertos para codificar en todos los módulos sin nombre , es decir, para codificar en el classpath. Si el sistema de tiempo de ejecución se invoca de esta manera, y si al hacerlo, algunas invocaciones de las API de reflexión tienen éxito donde de otro modo habrían fallado.

En tales casos, en realidad ha terminado haciendo un acceso reflectante que es "ilegal" ya que en un mundo modular puro no estaba destinado a hacer tales accesos.

¿Cómo encaja todo y qué desencadena la advertencia en qué escenario?

Esta relajación de la encapsulación se controla en tiempo de ejecución mediante una nueva opción --illegal-accessde iniciador que, por defecto, en Java9 es igual permit. El permitmodo asegura

La primera operación de acceso reflectante a cualquier paquete de este tipo hace que se emita una advertencia, pero no se emiten advertencias después de ese punto. Esta única advertencia describe cómo habilitar más advertencias. Esta advertencia no se puede suprimir.

Los modos se pueden configurar con valores debug(mensaje y seguimiento de pila para cada acceso), warn(mensaje para cada acceso) y deny(deshabilita tales operaciones).


Algunas cosas para depurar y corregir en aplicaciones serían: -

  • Ejecútelo con --illegal-access=denypara conocer y evitar abrir paquetes de un módulo a otro sin una declaración de módulo que incluya dicha directiva ( opens) o el uso explícito de --add-opensVM arg.
  • Las referencias estáticas del código compilado a las API internas de JDK se pueden identificar utilizando la jdepsherramienta con la --jdk-internalsopción

El mensaje de advertencia que se emite cuando se detecta una operación de acceso reflectante ilegal tiene la siguiente forma:

WARNING: Illegal reflective access by $PERPETRATOR to $VICTIM

dónde:

$PERPETRATOR es el nombre completo del tipo que contiene el código que invocó la operación de reflexión en cuestión más la fuente del código (es decir, la ruta del archivo JAR), si está disponible, y

$VICTIM es una cadena que describe el miembro al que se accede, incluido el nombre completo del tipo adjunto

Preguntas para este ejemplo de advertencia: = JDK9: Se ha producido una operación de acceso reflectante ilegal. org.python.core.PySystemState

Por último, y una nota importante, mientras trata de asegurarse de que no se enfrenta a tales advertencias y está a salvo en el futuro, todo lo que necesita hacer es asegurarse de que sus módulos no hagan esos accesos reflectantes ilegales. :)

Naman
fuente
21

Hay un artículo de Oracle que encontré sobre el sistema de módulos Java 9

De forma predeterminada, un tipo en un módulo no es accesible para otros módulos a menos que sea un tipo público y usted exporte su paquete. Expone solo los paquetes que desea exponer. Con Java 9, esto también se aplica a la reflexión.

Como se señala en https://stackoverflow.com/a/50251958/134894 , las diferencias entre AccessibleObject#setAccessibleJDK8 y JDK9 son instructivas. Específicamente, JDK9 agregó

Este método puede ser utilizado por una persona que llama en la clase C para permitir el acceso a un miembro de la clase D declarante si se cumple alguno de los siguientes:

  • C y D están en el mismo módulo.
  • El miembro es público y D es público en un paquete que el módulo que contiene D exporta al menos al módulo que contiene C.
  • El miembro está protegido estático, D es público en un paquete que el módulo que contiene D exporta al menos al módulo que contiene C, y C es una subclase de D.
  • D está en un paquete en el que el módulo que contiene D abre al menos el módulo que contiene C. Todos los paquetes en módulos abiertos y sin nombre están abiertos para todos los módulos, por lo que este método siempre tiene éxito cuando D está en un módulo abierto o sin nombre.

que destaca la importancia de los módulos y sus exportaciones (en Java 9)

ptomli
fuente
2
Entonces, si leo ese artículo, la modificación correcta de las propiedades privadas en las clases exportadas está fuera de la mesa. Solo se pueden modificar las propiedades públicas y protegidas. Ahora no me importan mucho las exportaciones internas de Java, sino más acerca de las bibliotecas de terceros, donde a veces necesito acceso a una variable privada para establecer un valor específico. Eso ya no sería posible en este esquema si se definiera a sí mismo como un módulo, ¿es correcto?
Tschallacka
1
No tengo experiencia directa con eso, pero ese sería mi entendimiento, y leer junto con el artículo mencionado en otra parte ( jaxenter.com/jdk-9-replace-permit-illegal-access-134180.html ) que parece ser el caso. Inicie su JVM con –illegal-access=permit...
ptomli
1
Bueno, eso hará que las cosas sean más interesantes tratando de hacer que las cosas funcionen para algunas cosas cuando decidan ir al módulo. Tiempos súper divertidos por delante.
Tschallacka
1
Para varios valores defun
ptomli
Acepté la otra respuesta porque proporcionaba una explicación más y era más una respuesta a la pregunta, pero lamentablemente no puedo aceptar dos respuestas.
Tschallacka
13

Solo mire el setAccessible()método utilizado para acceder a los privatecampos y métodos:

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-

Ahora se requieren muchas más condiciones para que este método funcione. La única razón por la que no rompe casi todo el software anterior es que los módulos generados automáticamente a partir de JAR simples son muy permisivos (abren y exportan todo para todos).

user158037
fuente
1

Si desea optar por la opción agregar-abrir, aquí hay un comando para encontrar qué módulo proporciona qué paquete ->

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

el nombre del módulo se mostrará con @ mientras que el nombre de los paquetes sin él

NOTA: probado con JDK 11

IMPORTANTE: obviamente es mejor que el proveedor del paquete no haga el acceso ilegal

Carlos Saltos
fuente