DEV Community

Cover image for [PT-BR] Reflection com JPMS
João Victor Martins
João Victor Martins

Posted on • Edited on

5

[PT-BR] Reflection com JPMS

Desde que surgiu o JPMS (Java Platform Module System), alguns comportamentos da plataforma sofreram alterações. Podemos citar como exemplo o uso de reflexão. Antes dos módulos, com a Reflection API, era possível quebrar o encapsulamento dos objetos e acessar seus atributos privados. Com o JPMS este comportamento mudou e agora o desenvolvedor precisa configurar o módulo para permitir reflexão, caso contrário uma exceção será lançada. Existem alguns detalhes por trás dessa configuração e a ideia é explorá-los no decorrer do post.

Vale destacar que, mesmo nas versões em que existe o JPMS (9+), a mudança citada acima só ocorrera se estiver compilando com module-path.

Existem duas formas para permitir reflexão. A primeira delas é usando a palavra reservada open na declaração do módulo, assim será permitido o uso de reflection em todos os pacotes.

open module br.com.exemplo {

}
Enter fullscreen mode Exit fullscreen mode

A outra opção é permitindo a reflexão por pacote, para isso usamos a palavra reservada opens antes da declaração do pacote.

module br.com.exemplo {
     opens br.com.exemplo.modelo;
}
Enter fullscreen mode Exit fullscreen mode

Qualquer tentativa de usar reflexão em um pacote que não seja o br.com.exemplo.modelo resultará em uma exceção.
Esta última opção ainda permite usar um opens declarativo, ou seja, informando qual módulo específico poderá realizar reflexão no pacote. Podemos fazer isso usando a palavra to

module br.com.exemplo {
     opens br.com.exemplo.modelo to br.com.outroexemplo.dao;
}
Enter fullscreen mode Exit fullscreen mode

Dessa maneira estamos dizendo que apenas o módulo br.com.outroexemplo.dao poderá fazer o uso de reflexão no pacote br.com.exemplo.modelo.

Vamos para a prática!!

Vamos usar a classe Principal do módulo br.com.principal para fazer um acesso reflexivo na classe Pessoa do módulo br.com.modelo.

package br.com.principal;

import java.lang.reflect.Field;
import br.com.modelo.Pessoa;

class Principal { 
    public static void main(String[] args) throws NoSuchFieldException {
        Field f = Pessoa.class.getDeclaredField("peso");
        f.setAccessible(true);
    }
}
Enter fullscreen mode Exit fullscreen mode

A classe Pessoa possui apenas um atributo privado.

package br.com.modelo;

public class Pessoa {
    private Double peso;
} 
Enter fullscreen mode Exit fullscreen mode

O módulo br.com.modelo exporta o pacote com a classe Pessoa

module br.com.modelo {
     exports br.com.modelo; 
}
Enter fullscreen mode Exit fullscreen mode

O módulo br.com.principal, faz o requires no módulo br.com.modelo

module br.com.principal {
     requires br.com.modelo;
} 
Enter fullscreen mode Exit fullscreen mode

Podemos compilar o projeto com javac -d mods --module-source-path src -m br.com.principal e se não deu nenhum problema, usamos o java --module-path mods -m br.com.principal/br.com.principal.Principal para executar o programa. Como não configuramos nossos módulos para permitir reflexão, a saída é a seguinte:

Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make field private java.lang.Double br.com.modelo.Pessoa.peso accessible: module br.com.modelo does not "opens br.com.modelo" to module br.com.principal
    at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:340)
    at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:280)
    at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:176)
    at java.base/java.lang.reflect.Field.setAccessible(Field.java:170)
    at br.com.principal/br.com.principal.Principal.main(Principal.java:9)
Enter fullscreen mode Exit fullscreen mode

Para mudar este comportamento, basta que alteremos o module-info.java do módulo br.com.modelo, permitindo que o módulo br.com.principal possa fazer um acesso reflexivo em seu pacote.

Aqui valem todas as regras citadas na introdução do post.

module br.com.modelo {
    exports br.com.modelo; 

    opens br.com.modelo to br.com.principal;
}
Enter fullscreen mode Exit fullscreen mode

Se compilar e executar novamente, vamos ver que não aparece mais a exceção e conseguimos usar a reflexão.

Por debaixo dos panos

A verificação, se permite reflexão, é feito pelo método setAccessible. Ele é um método caller sensitive e sua característica é ter comportamentos diferentes dependendo de quem o chama. Quando a classe ou módulo deseja utilizar reflexão em um outro módulo, é verificado se existe a palavra reservada open ou opens no módulo de destino. Caso tenha, o acesso é feito com sucesso, caso contrário o resultado é uma exceção. Para saber quando um método é caller sensitive, basta procurar pela anotação @CallerSensitive.

Concluindo ...

Nós vimos que, com o JPMS, as nossas classes ficam mais protegidas, garantindo um melhor encapsulamento. Caso seja necessário o uso de reflection, cabe a nós realizar as configurações nos módulos. Todo este comportamento é garantido por um método caller sensitive, que verificará se o módulo ou classe "chamadora" possui permissão para um acesso reflexivo no módulo de destino. A ideia era mostrar um pouco mais desses detalhes e espero ter alcançado, mas se ficou alguma dúvida, deixe seu comentário por aqui ou nas minhas redes sociais, que estarei a disposição. Valeu! =)

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read full post →

Top comments (2)

Collapse
 
cpdaniiel profile image
Daniel Cavalcanti

Ótimo conteúdo, excelente explicação.. Muito obrigado.. 😉

Collapse
 
freeluke_ profile image
Lukas Henrique

Essa explicação facilitou muito a minha vida! Escrita de fácil entendimento! Obrigado!

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more