W3docs

Tipos de Módulos Java

Módulos nomeados, automáticos e sem nome em Java e como eles interagem durante a compilação e a execução.

O Java Platform Module System (JPMS) reconhece três tipos de módulo. Apenas um é o módulo "real" que você escreve; os outros dois existem para que os milhões de JARs anteriores ao Java 9 continuem funcionando. Entender em qual tipo um determinado JAR se torna — e que isso depende inteiramente de onde você o coloca — é a chave para migrar sem dor. Esta página define os três tipos, mostra as regras de acesso entre eles e comprova as categorias com um programa executável.

Módulos nomeados

Um módulo nomeado (explícito) é aquele que possui um module-info.class, colocado no module path (--module-path / -p). Ele é o cidadão completo:

  • Tem um nome proveniente do seu descritor.
  • apenas os módulos que declara em requires.
  • Expõe apenas os pacotes que declara em exports.

Este é o módulo com encapsulamento forte descrito no capítulo sobre declaração de módulos. Tudo que o JPMS promete — dependências declaradas, internos ocultos, resolução com falha rápida — aplica-se aos módulos nomeados.

Módulos automáticos

Um módulo automático é um JAR simples (sem module-info) colocado no module path. O JPMS o envolve em um módulo para que módulos nomeados possam declará-lo em requires durante a migração — sem precisar aguardar o autor da biblioteca adicionar um descritor. Um módulo automático:

  • Recebe um nome derivado do nome do arquivo JAR (por exemplo, guava-32.1.jarguava), a menos que o manifesto do JAR defina Automatic-Module-Name.
  • Exporta todos os pacotes — não possui diretiva exports, portanto todos os seus pacotes estão abertos para o mundo.
  • Lê todos os outros módulos, incluindo o módulo sem nome, para que ainda possa enxergar os JARs do classpath.

Ele é uma ponte: permite que você comece a escrever módulos nomeados que dependem de bibliotecas ainda não modularizadas. O custo é que abre mão completamente do encapsulamento, e seu nome derivado automaticamente pode mudar se o JAR for renomeado — motivo pelo qual Automatic-Module-Name no manifesto é o caminho responsável para uma biblioteca adotar.

O módulo sem nome

O módulo sem nome é o repositório geral do classpath. Toda classe carregada do classpath pertence ao módulo sem nome do seu class loader. Ele:

  • Não tem nome (getName() retorna null, isNamed() é false).
  • Lê todos os outros módulos do sistema.
  • Exporta todos os seus pacotes para outros módulos sem nome e automáticos.

Mas existe uma parede deliberada em um único sentido: um módulo nomeado não pode declarar requires ao módulo sem nome. Você não pode nomeá-lo e, portanto, não pode depender dele. Esta é a regra que determina a ordem de migração — um módulo nomeado só pode depender de outros módulos nomeados ou automáticos, nunca de código bruto do classpath.

A matriz de acesso

Quem pode ler quem se resume a uma pequena tabela:

De ↓ / Para →NomeadoAutomáticoSem nome
Nomeadosomente se requiressomente se requiresnunca
Automáticosimsimsim
Sem nomesimsimsim

A única célula restritiva — código nomeado não pode alcançar código sem nome — é toda a história de por que a migração é feita de baixo para cima (abordado no próximo capítulo).

Um exemplo prático: identificando o tipo de um módulo em tempo de execução

A API de Module informa, para qualquer classe, se o seu módulo é nomeado e se foi sintetizado automaticamente. Este programa inspeciona três referências — sua própria classe (classpath → sem nome), um tipo do JDK (nomeado) e reporta a camada de boot — para tornar as categorias concretas.

java— editable, runs on the server

O que concluir da execução:

  • A própria classe do programa foi classificada como UNNAMED com nome null, enquanto java.util.List e HttpClient foram classificados como NAMED (java.base, java.net.http). Ao executar a partir do classpath, seu código é sempre sem nome; o JDK é sempre um conjunto de módulos nomeados. O tipo de um módulo é determinado por como ele foi carregado, não por nada na própria classe.
  • java.base.canRead(self) retornou false, mas self.canRead(java.base) retornou true. Essa é a parede unidirecional em ação: o módulo sem nome lê tudo, mas nenhum módulo nomeado lê o módulo sem nome. Essa assimetria é precisamente o motivo pelo qual código nomeado não pode declarar requires ao código do classpath.
  • classify() distinguiu automático de nomeado por meio de descriptor.isAutomatic(). Você não verá true aqui (nada foi colocado no module path como um JAR simples), mas a verificação é exatamente como as ferramentas relatam um módulo automático — um objeto de módulo real com um descritor sintetizado e totalmente aberto.
  • isExported("java.util") retornou true, mas isExported("jdk.internal.misc") retornou false, mesmo que ambos sejam pacotes reais dentro de java.base. O exports de um módulo nomeado é uma lista de permissões; pacotes não exportados (ou exportados apenas de forma qualificada) são invisíveis para código externo, independentemente de serem public. O módulo sem nome, por outro lado, exporta tudo que contém.
  • Nenhum module-info.java foi necessário para observar nada disso. As três categorias são fatos de tempo de execução sobre como uma classe foi carregada, e getModule() junto com getDescriptor() os expõem — as mesmas chamadas que as ferramentas de migração utilizam para descobrir com o que estão trabalhando.

Por que existem três tipos

Os dois tipos de compatibilidade — automático e sem nome — significam que o Java 9+ executa aplicações Java 8 sem modificações. Você opta pelo encapsulamento forte um JAR por vez: deixe tudo no classpath (tudo sem nome) e nada muda; mova uma biblioteca para o module path sem um descritor e ela se torna automática; adicione um module-info.java e ela se torna nomeada. A seguir, os serviços de módulos apresentam o mecanismo uses/provides que desacopla módulos, e a migração de módulos conduz um projeto real por esses três estados.

Prática

Prática
Durante a migração você coloca um JAR de terceiros simples (sem 'module-info.class') chamado 'fastjson-2.0.jar' no MODULE PATH e depois escreve 'requires fastjson;' no seu próprio módulo nomeado. Qual afirmação descreve corretamente o 'fastjson' aqui?
Durante a migração você coloca um JAR de terceiros simples (sem 'module-info.class') chamado 'fastjson-2.0.jar' no MODULE PATH e depois escreve 'requires fastjson;' no seu próprio módulo nomeado. Qual afirmação descreve corretamente o 'fastjson' aqui?
Was this page helpful?