qu'est-ce qu'un accès réflexif illégal

126

Il y a beaucoup de questions autour de l'accès réflexif illégal dans Java 9.

Maintenant, ce que je ne peux pas trouver, car tout ce que Google crache, ce sont des gens qui essaient de contourner les messages d'erreur, c'est ce qu'est en réalité un accès réflexif illégal.

Ma question est donc assez simple:

Qu'est-ce qui définit un accès réflexif illégal et quelles circonstances déclenchent l'avertissement?

J'ai compris que cela a quelque chose à voir avec les principes d'encapsulation qui ont été introduits dans Java 9, mais comment tout cela se tient ensemble et ce qui déclenche l'avertissement dans quel scénario je ne trouve pas d'explication.

Tschallacka
la source
2
cela peut également vous intéresser: jaxenter.com/jdk-9-replace-permit-illegal-access-134180.html
Edwin

Réponses:

54

Outre une compréhension des accès entre les modules et leurs packages respectifs. Je crois que l'essentiel réside dans le système de module # Encapsulation forte-détendue et je choisirais simplement les parties pertinentes pour essayer de répondre à la question.

Qu'est-ce qui définit un accès réflexif illégal et quelles circonstances déclenchent l'avertissement?

Pour faciliter la migration vers Java-9, l'encapsulation forte des modules pourrait être assouplie.

  • Une implémentation peut fournir un accès statique , c'est-à-dire par bytecode compilé.

  • Peut fournir un moyen d'invoquer son système d'exécution avec un ou plusieurs packages d'un ou plusieurs de ses modules ouverts au code dans tous les modules sans nom , c'est-à-dire au code sur le chemin de classe. Si le système d'exécution est appelé de cette manière, et si, ce faisant, certaines invocations des API de réflexion réussissent, sinon elles auraient échoué.

Dans de tels cas, vous avez fini par créer un accès réfléchi qui est "illégal" puisque dans un monde purement modulaire, vous n'étiez pas censé faire de tels accès.

Comment tout cela s'enchaîne et qu'est-ce qui déclenche l'avertissement dans quel scénario?

Cette relaxation de l'encapsulation est contrôlée à l'exécution par une nouvelle option de lanceur --illegal-accessqui par défaut en Java9 est égale permit. Le permitmode assure

La première opération d'accès réfléchissant à un tel paquet provoque l'émission d'un avertissement, mais aucun avertissement n'est émis après ce point. Cet avertissement unique décrit comment activer d'autres avertissements. Cet avertissement ne peut pas être supprimé.

Les modes sont configurables avec des valeurs debug(message ainsi que stacktrace pour chacun de ces accès), warn(message pour chacun de ces accès) et deny(désactive ces opérations).


Peu de choses à déboguer et à corriger sur les applications seraient: -

  • Exécutez-le avec --illegal-access=denypour connaître et éviter d' ouvrir des packages d'un module à un autre sans déclaration de module incluant une telle directive ( opens) ou utilisation explicite de --add-opensVM arg.
  • Les références statiques du code compilé aux API internes JDK peuvent être identifiées à l'aide de l' jdepsoutil avec l' --jdk-internalsoption

Le message d'avertissement émis lorsqu'une opération d'accès réfléchissant illégale est détectée a la forme suivante:

WARNING: Illegal reflective access by $PERPETRATOR to $VICTIM

où:

$PERPETRATOR est le nom complet du type contenant le code qui a invoqué l'opération réfléchissante en question plus la source du code (c'est-à-dire le chemin du fichier JAR), si disponible, et

$VICTIM est une chaîne qui décrit le membre auquel accède, y compris le nom complet du type englobant

Questions pour un tel exemple d'avertissement: = JDK9: une opération d'accès réfléchissant illégale s'est produite. org.python.core.PySystemState

Enfin et une note importante, tout en essayant de vous assurer que vous ne faites pas face à de tels avertissements et que vous êtes en sécurité pour l'avenir, tout ce que vous avez à faire est de vous assurer que vos modules n'effectuent pas ces accès réfléchissants illégaux. :)

Naman
la source
21

J'ai trouvé un article Oracle concernant le système de modules Java 9

Par défaut, un type dans un module n'est pas accessible aux autres modules sauf s'il s'agit d'un type public et que vous exportez son package. Vous exposez uniquement les packages que vous souhaitez exposer. Avec Java 9, cela s'applique également à la réflexion.

Comme indiqué dans https://stackoverflow.com/a/50251958/134894 , les différences entre le AccessibleObject#setAccessiblepour JDK8 et JDK9 sont instructives. Plus précisément, JDK9 a ajouté

Cette méthode peut être utilisée par un appelant de la classe C pour permettre l'accès à un membre de la classe déclarante D si l'une des conditions suivantes est maintenue:

  • C et D sont dans le même module.
  • Le membre est public et D est public dans un package que le module contenant D exporte vers au moins le module contenant C.
  • Le membre est protégé statique, D est public dans un package que le module contenant D exporte vers au moins le module contenant C, et C est une sous-classe de D.
  • D est dans un package que le module contenant D ouvre au moins au module contenant C. Tous les packages dans les modules sans nom et ouverts sont ouverts à tous les modules et donc cette méthode réussit toujours lorsque D est dans un module sans nom ou ouvert.

qui met en évidence l'importance des modules et de leurs exportations (en Java 9)

ptomli
la source
2
Donc, si je lis cet article correctement, modifier les propriétés privées dans les classes exportées est hors de propos. Seules les propriétés protégées et publiques peuvent être modifiées. Maintenant, je ne me soucie pas beaucoup des exportations internes java, mais plus des bibliothèques tierces où j'ai parfois besoin d'accéder à une variable privée pour être définie sur une valeur spécifique. Ce ne serait plus possible dans ce schéma s'il se définissait comme un module, est-ce exact?
Tschallacka
1
Je n'ai pas d'expérience directe avec cela, mais ce serait ce que je comprends, et lire à côté de l'article mentionné ailleurs ( jaxenter.com/jdk-9-replace-permit-illegal-access-134180.html ) qui semble être l'affaire. Lancez votre JVM avec –illegal-access=permit...
ptomli
1
Eh bien, cela va rendre les choses plus intéressantes en essayant de faire fonctionner les choses pour certaines choses quand ils décident de passer au module. Des moments super amusants à venir.
Tschallacka
1
Pour diverses valeurs defun
ptomli
J'ai accepté l'autre réponse car elle a fourni une explication supplémentaire et était plus une réponse à la question, mais malheureusement je ne peux pas accepter deux réponses.
Tschallacka
13

Regardez simplement la setAccessible()méthode utilisée pour accéder aux privatechamps et aux méthodes:

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-

Maintenant, il y a beaucoup plus de conditions requises pour que cette méthode fonctionne. La seule raison pour laquelle il ne casse pas presque tous les anciens logiciels est que les modules générés automatiquement à partir de JAR simples sont très permissifs (tout ouvrir et exporter pour tout le monde).

user158037
la source
1

Si vous souhaitez utiliser l'option add-open, voici une commande pour trouver quel module fournit quel package ->

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

le nom du module sera affiché avec le @ tandis que le nom des packages sans lui

REMARQUE: testé avec JDK 11

IMPORTANT: c'est évidemment mieux que le fournisseur du package ne fasse pas l'accès illégal

Carlos Saltos
la source