o que é um acesso reflexivo ilegal

126

Existem muitas perguntas sobre o acesso reflexivo ilegal no Java 9.

Agora, o que não consigo encontrar porque tudo o que o Google expele são pessoas tentando contornar as mensagens de erro, é o que um acesso reflexivo ilegal realmente é.

Portanto, minha pergunta é bastante simples:

O que define um acesso reflexivo ilegal e quais circunstâncias acionam o aviso?

Concluí que tem algo a ver com os princípios de encapsulamento que foram introduzidos no Java 9, mas como tudo se encaixa e o que dispara o aviso em qual cenário não consigo encontrar uma explicação.

Tschallacka
fonte
2
isso pode interessar a você também: jaxenter.com/jdk-9-replace-permit-illegal-access-134180.html
Edwin

Respostas:

54

Além de compreender os acessos entre os módulos e seus respectivos pacotes. Acredito que o ponto crucial disso está no Módulo System # Relaxed-strong-encapsulation e eu selecionaria as partes relevantes para tentar responder à pergunta.

O que define um acesso reflexivo ilegal e quais circunstâncias acionam o aviso?

Para ajudar na migração para Java-9, o encapsulamento forte dos módulos pode ser relaxado.

  • Uma implementação pode fornecer acesso estático , ou seja, por bytecode compilado.

  • Pode fornecer um meio de invocar seu sistema de tempo de execução com um ou mais pacotes de um ou mais de seus módulos abertos para código em todos os módulos não nomeados , ou seja, para código no classpath. Se o sistema de tempo de execução for invocado dessa forma, e ao fazer isso, algumas invocações das APIs de reflexão serão bem-sucedidas onde, de outra forma, teriam falhado.

Nesses casos, você acabou fazendo um acesso reflexivo que é "ilegal", já que em um mundo modular puro você não deveria fazer tais acessos.

Como tudo se encaixa e o que dispara o aviso em qual cenário?

Esse relaxamento do encapsulamento é controlado no tempo de execução por uma nova opção do ativador --illegal-accessque, por padrão, no Java9 é igual permit. O permitmodo garante

A primeira operação de acesso reflexivo a qualquer um desses pacotes faz com que um aviso seja emitido, mas nenhum aviso é emitido após esse ponto. Este único aviso descreve como habilitar outros avisos. Este aviso não pode ser suprimido.

Os modos são configuráveis ​​com valores debug(mensagem, bem como rastreamento de pilha para cada acesso), warn(mensagem para cada acesso) e deny(desativa essas operações).


Poucas coisas para depurar e corrigir nos aplicativos seriam: -

  • Execute-o com --illegal-access=denypara conhecer e evitar a abertura de pacotes de um módulo para outro sem uma declaração de módulo incluindo tal diretiva ( opens) ou o uso explícito de --add-opensVM arg.
  • As referências estáticas do código compilado para APIs internas do JDK podem ser identificadas usando a jdepsferramenta com a --jdk-internalsopção

A mensagem de aviso emitida quando uma operação de acesso reflexivo ilegal é detectada tem o seguinte formato:

WARNING: Illegal reflective access by $PERPETRATOR to $VICTIM

Onde:

$PERPETRATOR é o nome totalmente qualificado do tipo que contém o código que invocou a operação reflexiva em questão mais o código-fonte (ou seja, caminho do arquivo JAR), se disponível, e

$VICTIM é uma string que descreve o membro que está sendo acessado, incluindo o nome totalmente qualificado do tipo envolvente

Perguntas para esse exemplo de aviso: = JDK9: Ocorreu uma operação de acesso reflexivo ilegal. org.python.core.PySystemState

Por último e uma observação importante, ao tentar garantir que você não enfrente tais avisos e esteja seguro no futuro, tudo que você precisa fazer é garantir que seus módulos não estejam fazendo aqueles acessos reflexivos ilegais. :)

Naman
fonte
21

Há um artigo da Oracle que encontrei sobre o sistema de módulos Java 9

Por padrão, um tipo em um módulo não é acessível a outros módulos, a menos que seja um tipo público e você exporte seu pacote. Você expõe apenas os pacotes que deseja expor. Com o Java 9, isso também se aplica à reflexão.

Conforme apontado em https://stackoverflow.com/a/50251958/134894 , as diferenças entre o AccessibleObject#setAccessiblepara JDK8 e JDK9 são instrutivas. Especificamente, JDK9 adicionado

Este método pode ser usado por um chamador na classe C para permitir o acesso a um membro da classe D declarante se qualquer um dos seguintes itens for mantido:

  • C e D estão no mesmo módulo.
  • O membro é público e D é público em um pacote que o módulo que contém D exporta para pelo menos o módulo que contém C.
  • O membro é protegido estático, D é público em um pacote que o módulo que contém D exporta para pelo menos o módulo que contém C e C é uma subclasse de D.
  • D está em um pacote que o módulo que contém D abre para pelo menos o módulo que contém C. Todos os pacotes em módulos não nomeados e abertos são abertos para todos os módulos e, portanto, este método sempre tem sucesso quando D está em um módulo não nomeado ou aberto.

que destaca a importância dos módulos e suas exportações (em Java 9)

ptomli
fonte
2
Portanto, se eu li esse artigo, modificar propriedades privadas em classes exportadas está fora de questão. Somente propriedades protegidas e públicas podem ser modificadas. Agora eu não me importo muito com as exportações internas de java, mas mais sobre bibliotecas de terceiros, onde às vezes preciso acessar uma variável privada a ser definida para um valor específico. Isso não seria mais possível neste esquema se ele se definisse como um módulo, correto?
Tschallacka
1
Não tenho experiência direta com isso, mas seria o meu entendimento, e li junto com o artigo mencionado em outro lugar ( jaxenter.com/jdk-9-replace-permit-illegal-access-134180.html ) que parece ser O caso. Inicie seu JVM com –illegal-access=permit...
ptomli
1
Bem, isso vai tornar as coisas mais interessantes tentar fazer as coisas funcionarem para algumas coisas quando decidirem ir para o caminho do módulo. Tempos super divertidos pela frente.
Tschallacka
1
Para vários valores defun
ptomli
Aceitei a outra resposta porque fornecia uma explicação mais detalhada e era mais uma resposta à pergunta, mas infelizmente não posso aceitar duas respostas.
Tschallacka
13

Basta olhar para o setAccessible()método usado para acessar privatecampos e 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-

Agora, são necessárias muito mais condições para que esse método funcione. A única razão pela qual ele não quebra quase todos os softwares mais antigos é que os módulos gerados automaticamente a partir de JARs simples são muito permissivos (abrem e exportam tudo para todos).

user158037
fonte
1

Se você quiser ir com a opção add-open, aqui está um comando para descobrir qual módulo fornece qual pacote ->

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

o nome do módulo será mostrado com @ enquanto o nome dos pacotes sem ele

NOTA: testado com JDK 11

IMPORTANTE: obviamente é melhor que o provedor do pacote não faça o acesso ilegal

Carlos Saltos
fonte