che cos'è un accesso riflessivo illegale


126

Ci sono molte domande in giro sull'accesso riflessivo illegale in Java 9.

Quello che non riesco a trovare perché tutto ciò che Google vomita sono persone che cercano di aggirare i messaggi di errore, è ciò che in realtà è un accesso riflessivo illegale.

Quindi la mia domanda abbastanza semplice è:

Cosa definisce un accesso riflessivo illegale e quali circostanze attivano l'avviso?

Ho capito che ha qualcosa a che fare con i principi di incapsulamento che sono stati introdotti in Java 9, ma come tutto si blocca e cosa fa scattare l'avviso in quale scenario non riesco a trovare una spiegazione.


Risposte:


54

A parte una comprensione degli accessi tra i moduli e i rispettivi pacchetti. Credo che il punto cruciale risieda nel Module System # Relaxed-strong-encapsulation e vorrei solo selezionare le parti rilevanti per cercare di rispondere alla domanda.

Cosa definisce un accesso riflessivo illegale e quali circostanze attivano l'avviso?

Per aiutare nella migrazione a Java-9, il forte incapsulamento dei moduli potrebbe essere allentato.

  • Un'implementazione può fornire l' accesso statico , cioè tramite bytecode compilato.

  • Può fornire un mezzo per invocare il suo sistema run-time con uno o più pacchetti di uno o più dei suoi moduli aperti al codice in tutti i moduli senza nome , cioè al codice sul classpath. Se il sistema di runtime viene richiamato in questo modo, e se così facendo alcune invocazioni delle API di riflessione hanno esito positivo dove altrimenti avrebbero fallito.

In questi casi, hai effettivamente finito per creare un accesso riflessivo che è "illegale" poiché in un mondo modulare puro non dovevi fare tali accessi.

Come tutto si blocca e cosa fa scattare l'avvertimento in quale scenario?

Questo rilassamento dell'incapsulamento è controllato in fase di esecuzione da una nuova opzione di avvio --illegal-accessche per impostazione predefinita in Java9 è uguale permit. La permitmodalità garantisce

La prima operazione di accesso riflessivo a qualsiasi pacchetto di questo tipo provoca l'emissione di un avviso, ma dopo quel punto non viene emesso alcun avviso. Questo singolo avviso descrive come abilitare ulteriori avvisi. Questo avviso non può essere soppresso.

Le modalità sono configurabili con valori debug(messaggio così come stacktrace per ogni accesso di questo tipo), warn(messaggio per ciascuno di questi accessi) e deny(disabilita tali operazioni).


Poche cose per eseguire il debug e correggere sulle applicazioni sarebbero: -

  • Eseguilo con --illegal-access=denyper conoscere ed evitare di aprire pacchetti da un modulo a un altro senza una dichiarazione del modulo che includa tale direttiva ( opens) o l'uso esplicito di --add-opensVM arg.
  • I riferimenti statici dal codice compilato alle API interne a JDK potrebbero essere identificati utilizzando lo jdepsstrumento con l' --jdk-internalsopzione

Il messaggio di avviso emesso quando viene rilevata un'operazione di accesso riflessivo illegale ha la seguente forma:

WARNING: Illegal reflective access by $PERPETRATOR to $VICTIM

dove:

$PERPETRATOR è il nome completo del tipo contenente il codice che ha richiamato l'operazione riflessiva in questione più il codice sorgente (cioè, il percorso del file JAR), se disponibile, e

$VICTIM è una stringa che descrive il membro a cui si accede, incluso il nome completo del tipo di inclusione

Domande per tale avviso di esempio: = JDK9: si è verificata un'operazione di accesso riflessivo illegale. org.python.core.PySystemState

Ultima e importante nota, mentre cerchi di assicurarti di non affrontare tali avvertimenti e di essere al sicuro in futuro, tutto ciò che devi fare è assicurarti che i tuoi moduli non stiano facendo quegli accessi riflessivi illegali. :)


21

C'è un articolo Oracle che ho trovato riguardo al sistema di moduli Java 9

Per impostazione predefinita, un tipo in un modulo non è accessibile ad altri moduli a meno che non sia un tipo pubblico e tu esporti il ​​suo pacchetto. Esponi solo i pacchetti che desideri esporre. Con Java 9, questo vale anche per la riflessione.

Come sottolineato in https://stackoverflow.com/a/50251958/134894 , le differenze tra AccessibleObject#setAccessibleJDK8 e JDK9 sono istruttive. Nello specifico, ha aggiunto JDK9

Questo metodo può essere utilizzato da un chiamante nella classe C per abilitare l'accesso a un membro della classe dichiarante D se una delle seguenti condizioni è presente:

  • C e D sono nello stesso modulo.
  • Il membro è pubblico e D è pubblico in un pacchetto che il modulo contenente D esporta almeno nel modulo contenente C.
  • Il membro è protetto statico, D è pubblico in un pacchetto che il modulo contenente D esporta almeno nel modulo contenente C e C è una sottoclasse di D.
  • D è in un pacchetto che il modulo contenente D apre almeno al modulo contenente C. Tutti i pacchetti nei moduli senza nome e aperti sono aperti a tutti i moduli e quindi questo metodo ha sempre successo quando D si trova in un modulo senza nome o aperto.

che evidenzia l'importanza dei moduli e delle loro esportazioni (in Java 9)


2
Quindi se leggo quell'articolo che modifica correttamente le proprietà private nelle classi esportate è fuori tabella. Possono essere modificate solo proprietà protette e pubbliche. Ora non mi interessa molto delle esportazioni di interni java, ma più delle librerie di terze parti in cui a volte ho bisogno di accedere a una variabile privata per essere impostata su un valore specifico. Ciò non sarebbe più possibile in questo schema se si definisse come un modulo, è corretto?
Tschallacka

1
Non ho esperienza diretta con questo, ma questa sarebbe la mia comprensione, e leggere accanto all'articolo menzionato altrove ( jaxenter.com/jdk-9-replace-permit-illegal-access-134180.html ) che sembrerebbe essere il caso. Avvia la tua JVM con –illegal-access=permit...
ptomli

1
Bene, questo renderà le cose più interessanti cercando di far funzionare le cose per alcune cose quando decidono di passare alla modalità modulo. Ci aspettano tempi super divertenti.
Tschallacka

1
Per vari valori difun
ptomli

Ho accettato l'altra risposta perché forniva una spiegazione in più ed era più una risposta alla domanda, ma purtroppo non posso accettare due risposte.
Tschallacka

13

Basta guardare il setAccessible()metodo utilizzato per accedere ai privatecampi e ai metodi:

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-

Ora ci sono molte più condizioni necessarie affinché questo metodo funzioni. L'unico motivo per cui non danneggia quasi tutto il software più vecchio è che i moduli generati automaticamente da JAR semplici sono molto permissivi (apri ed esporta tutto per tutti).


1

Se vuoi usare l'opzione aggiungi-apri, ecco un comando per trovare quale modulo fornisce quale pacchetto ->

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

il nome del modulo verrà mostrato con la @ mentre il nome dei pacchetti senza di essa

NOTA: testato con JDK 11

IMPORTANTE: ovviamente è meglio che il fornitore del pacchetto non faccia l'accesso illegale

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.