W3docs

Declaração Java module-info.java

Declare um módulo Java com module-info.java — requires, exports, opens, uses, provides.

Um módulo é declarado em um arquivo-fonte especial, module-info.java, colocado na raiz da árvore de fontes do módulo (ao lado do pacote principal, não dentro dele). Ele é compilado para module-info.class. O arquivo não contém código comum — apenas um bloco module listando diretivas que descrevem a fronteira do módulo.

A estrutura do arquivo

module com.acme.orders {
    requires com.acme.common;       // I depend on this module
    requires transitive java.sql;   // ...and so does anyone who requires me

    exports com.acme.orders.api;            // public to everyone
    exports com.acme.orders.spi to com.acme.web;  // public to one module only

    opens com.acme.orders.model;    // deep reflective access (e.g. for Jackson)

    uses com.acme.orders.PricingRule;            // I consume this service
    provides com.acme.orders.PricingRule          // I supply an implementation
        with com.acme.orders.StandardPricing;
}

O nome do módulo (com.acme.orders) é um identificador pontilhado, convencionalmente o prefixo DNS reverso dos pacotes que ele contém. Ele não é um pacote — é seu próprio namespace, e dois módulos não podem compartilhar um pacote.

requires — declarando dependências

requires <module> significa "preciso dos pacotes exportados desse módulo para compilar e executar." O resolvedor falha na inicialização se um módulo requerido estiver ausente. Dois modificadores importantes:

  • requires transitivereexporta a dependência. Qualquer módulo que requeira você automaticamente também a lê. Use-o quando os tipos de um módulo requerido aparecem nas suas próprias assinaturas públicas (um método que retorna um java.sql.Connection força os chamadores a enxergar java.sql).
  • requires static — uma dependência somente em tempo de compilação, opcional em tempo de execução (para processadores de anotação, integrações opcionais).

java.base é requerido implicitamente; você nunca o escreve.

exports — declarando sua API pública

exports <package> torna os tipos public desse pacote visíveis a outros módulos. Tudo que não for exportado está fortemente encapsulado — invisível mesmo sendo public. A forma qualificada, exports <package> to <module>, <module>, restringe a visibilidade a uma lista de permissões nomeada, útil para pacotes SPI compartilhados apenas entre seus próprios módulos.

Note que exports é por pacote, nunca recursivo: exportar com.acme.api não exporta com.acme.api.internal.

opens — permitindo reflexão profunda

exports concede acesso em tempo de compilação a membros public. Ele não concede acesso reflexivo a membros não públicos. Frameworks como Jackson, Hibernate e Spring usam setAccessible(true) para alcançar campos private — isso requer opens:

  • opens <package> — concede acesso reflexivo em tempo de execução (incluindo membros private) a todos os módulos.
  • opens <package> to <module> — qualificado, apenas para módulos nomeados.
  • open module com.acme.orders { … } — abre todos os pacotes (um auxílio de migração abrangente).

A distinção importa: você exports um pacote de API, mas usa opens em um pacote de classes de dados que deseja que um serializador reflita sem torná-lo parte da sua API em tempo de compilação.

uses / provides — serviços

Esses conectam o padrão ServiceLoader: uses <Service> declara que você consome uma interface de serviço, e provides <Service> with <Impl> declara uma implementação. Eles têm seu próprio capítulo — veja Serviços de módulo — aqui apenas note que eles residem no mesmo descritor.

Um exemplo trabalhado: construindo um descritor com a API

Normalmente você escreve module-info.java e deixa o javac produzir o descritor. Mas a mesma estrutura está disponível programaticamente por meio de ModuleDescriptor.newModule(...), que é um espelho fiel da sintaxe de diretivas — construir um é a maneira mais clara de ver o que cada diretiva se torna.

java— editable, runs on the server

O que observar na execução:

  • Os nomes dos métodos do construtor correspondem um a um com as diretivas: .requires(...), .exports(...), .opens(...), .uses(...), .provides(...). Lendo a saída, o descritor é exatamente a informação em um module-info.java — prova de que o arquivo é metadado puro, não código executável.
  • O require de java.sql foi impresso com um modificador [TRANSITIVE] enquanto com.acme.common foi impresso sem nenhum. Esse modificador é o que reexporta java.sql para módulos downstream; o require simples mantém a dependência privada para este módulo.
  • As duas exportações foram impressas de forma diferente: com.acme.orders.api como "(to all)" e com.acme.orders.spi como "to [com.acme.web]". Uma exportação qualificada carrega sua lista de permissões de destino dentro do descritor — o resolvedor a aplica, portanto nenhum outro módulo pode ler o pacote SPI.
  • opens apareceu em sua própria seção, separado de exports. O descritor mantém a exposição em tempo de compilação e o acesso reflexivo em tempo de execução como fatos distintos, e é por isso que um serializador precisa de opens mesmo quando o pacote já está exportado.
  • uses e provides são registrados ao lado do restante — as declarações de serviço fazem parte da fronteira do módulo, não um arquivo de configuração separado como eram no classpath (META-INF/services). Serviços de módulo transforma essas diretivas em um ServiceLoader funcional.

Erros comuns

  • Colocar module-info.java dentro de um diretório de pacote — ele deve ficar na raiz do código-fonte.
  • Confundir exports (tempo de compilação, membros públicos) com opens (tempo de execução, todos os membros). Um JsonMappingException sobre um campo inacessível quase sempre significa um opens ausente.
  • Esquecer requires transitive quando seus métodos públicos expõem tipos de outro módulo, forçando cada chamador a adicionar o require manualmente.

O próximo capítulo, Tipos de módulo, recua para os três tipos de módulo — nomeado, automático e não nomeado — e como uma aplicação parcialmente modularizada continua funcionando enquanto você migra para módulos. Para uma visão mais ampla de por que o sistema de módulos existe, veja a introdução aos módulos.

Prática

Prática
Um módulo 'com.acme.orders' tem um método público que retorna um 'java.sql.Connection'. Chamadores em outros módulos continuam falhando na compilação porque não conseguem ver o tipo 'java.sql.Connection', mesmo já tendo 'requires com.acme.orders'. Qual diretiva em 'com.acme.orders' resolve isso sem forçar cada chamador a adicionar 'requires java.sql' manualmente?
Um módulo 'com.acme.orders' tem um método público que retorna um 'java.sql.Connection'. Chamadores em outros módulos continuam falhando na compilação porque não conseguem ver o tipo 'java.sql.Connection', mesmo já tendo 'requires com.acme.orders'. Qual diretiva em 'com.acme.orders' resolve isso sem forçar cada chamador a adicionar 'requires java.sql' manualmente?
Was this page helpful?